<?php
//======================================================================================
//
// Function: Main menu for GPS
//
// Programmer: AR
// Date : 2024-10-08
//
// Copyright Reeft A/S (c) - 2024
//======================================================================================
header("Access-Control-Allow-Origin: *"); // Or specify a specific origin
header("Access-Control-Allow-Methods: POST, GET, OPTIONS");
header("Access-Control-Allow-Headers: Content-Type, Authorization");
// Handle preflight requests
if ($_SERVER['REQUEST_METHOD'] === 'OPTIONS') {
http_response_code(200);
exit;
}
//======================================================================================
// Set session
//======================================================================================
if(!isset($_SESSION))
{
session_start();
}
//======================================================================================
// General
//======================================================================================
include "include/apikey.php";
include "include/config.php";
// Check if the token is already in the session
if (!isset($_SESSION['TargetOrganization']) || !isset($_REQUEST['TargetOrganization']) || $_SESSION['TargetOrganization'] != $_REQUEST['TargetOrganization']) {
// Include the token validation
include_once "token_validation.php";
}
//======================================================================================
// Language data ["en", "de", "da", "no", "sv"]
//======================================================================================
$loginLanguage = $_SESSION['loginLanguage'];
include "language/$loginLanguage.php";
?>
<!DOCTYPE html>
<html lang="en">
<head>
<title>GPS</title>
<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
<meta name="description" content="REEFT GPS">
<meta name="author" content="REEFT A/S">
<link rel="icon" href="images/favicon/favicon.ico">
<link href="javascript/bootstrap-5.3.3/css/bootstrap.min.css" rel="stylesheet">
<link href="javascript/chosen_v1.8.7/chosen.css" rel="stylesheet" type="text/css">
<link rel="stylesheet" href="javascript/jquery-ui-1.14.1/jquery-ui.css">
<link href="javascript/fontawesome/6.5.1/css/all.min.css" rel="stylesheet" type="text/css">
<link href="javascript/fontawesome/6.5.1/css/sharp-solid.min.css" rel="stylesheet" type="text/css">
<link href="css/sticky-footer.css" rel="stylesheet" type="text/css">
<script src="javascript/jquery-3.7.1.min.js"></script>
<script src="javascript/jquery-ui-1.14.1/jquery-ui.min.js"></script>
<script src="javascript/popper.min.js"></script>
<script src="javascript/ajaxq/ajaxq.js"></script>
<script src="javascript/bootstrap-5.3.3/js/bootstrap.min.js"></script>
<script src="javascript/chosen_v1.8.7/chosen.jquery.min.js" type="text/javascript"></script>
<script type="text/javascript" src="javascript/markerclusterer.min.js"></script>
<script type="text/javascript" src="javascript/infobubble.js"></script>
<script type="text/javascript" src="javascript/xlxs-0.15.6/xlsx.full.min.js"></script>
<link href="css/main.css" rel="stylesheet">
<!-- Loads the Google Maps API script asynchronously, the script runs only after the HTML document has been fully parsed -->
<script src="https://maps.googleapis.com/maps/api/js?libraries=geometry&key=<?php echo $apikey; ?>&callback=initMap&v=<?php echo time(); ?>" async defer></script>
<script language="JavaScript">
//=============================================================================
// Globals
//=============================================================================
//variables from REEFT 2.0
var token = '<?php echo $_SESSION['token']; ?>';
var loginOrganizationId = '<?php echo $_SESSION['loginOrganizationId']; ?>';
var loginOrganizationName = '<?php echo $_SESSION['loginOrganizationName']; ?>';
var loginUserId = '<?php echo $_SESSION['loginUserId']; ?>';
var loginUserName = '<?php echo $_SESSION['loginUserName']; ?>';
var loginUserRole = '<?php echo $_SESSION['loginUserRole']; ?>';
var loginDepartmentId = '<?php echo $_SESSION['loginDepartmentId']; ?>';
var loginDepartmentName = '<?php echo $_SESSION['loginDepartmentName']; ?>';
var dftMapCenterLat = <?php echo $DFT_MAP_CENTER_LAT; ?>;
var dftMapCenterLng = <?php echo $DFT_MAP_CENTER_LNG; ?>;
var dftZoomlevel = <?php echo $DFT_ZOOMLEVEL; ?>;
var gsmKey = '<?php echo $gsmKey; ?>';
var onlineLegend = '<?php echo $locale_text["GPSMAP_online_legend"]; ?>';
var yellowLegend = '<?php echo $locale_text["GPSMAP_yellow_legend"]; ?>';
var offlineLegend = '<?php echo $locale_text["GPSMAP_offline_legend"]; ?>';
var imeiList = [];
var carData = [];
var empJobList = [];
var jobtooltip = $("<div id='tooltip'><div class='mousehovercontent'/></div>");
var toolTipTimer = "";
var haveTooltip = false;
var custPopoverVisible = false; // Tracks the visibility of the popover
var jobPopoverVisible = false; // Tracks the visibility of the popover
var lastUpdated = Date.now(); // Last provided UNIX timestamp to getlocations
var inputTimer;
var lastSearchString = "";
var custQueueCount = 0;
var excelData = []; // Variable to store data to export to excel
var excelKeys = {}; // keys to lockup in excelData
var mapGlobals = {};
mapGlobals.map; // Google map obj.
mapGlobals.markerCluster; // Google map marker cluster
mapGlobals.infoBubble ; // InfoBubble at cluster
mapGlobals.allMarkers = {}; // Contains a list to store all markers
mapGlobals.infoWin; // Google infowindow object, reused for all info windows
mapGlobals.JSON_employeeList; // Contains a list of employees
mapGlobals.JSON_gpsTransactions; // Contains GPS transaction (latlng) for an employee
mapGlobals.JSON_customerList; // Contains a list of customers that have been requested through customer search.
mapGlobals.JSON_customerListID = new Array(); // Contains a list of id for customers
mapGlobals.JSON_serviceUnitID = new Array(); // Contains a list of id for serviceunits
mapGlobals.JSON_locationList = {}; // Contains a list of car locations
mapGlobals.JSON_jobList = {}; // Contains a list of jobs locations
mapGlobals.gpsEmployeeMarkersArray = new Array(); // Contains employee markers that are displayed
mapGlobals.addressMarker; // Contains marker object from address search
mapGlobals.gpsEmpLocationMarkers = new Array(); // Contains locations markers for an employee
mapGlobals.custSearching = false; // Determines if a cutomer search is in progress, used for controling enter key press behaviour
mapGlobals.currentCustomerMarker; // Selected customer marker obj
mapGlobals.selectedCustomer = ""; // Selected customer in customer section
mapGlobals.selectedJobs = new Array(); // Contains job selected in list.
mapGlobals.selectedMachines = new Array(); // Contains machines selected in list.
mapGlobals.selectedEmployees = new Array(); // Contains employees selected in list. Used for keeping state when refreshing map.
mapGlobals.routePolyline; // Polyline object for a route
mapGlobals.routeArray; // Contains routes
const jobStatusText = <?php echo json_encode($locale_text["JOB_STATUS"]); ?>;
const timesheetText = <?php echo json_encode($locale_text["TIMESHEET_STATUS"]); ?>;
var hoverTimeout;
//get initial data from REEFT 2.0
getEmeiList();
getEmployeeList();
getDepartmentList();
//=============================================================================
// Get a JSON list of employees with emei
//=============================================================================
function getEmeiList(){
$.ajaxq('getQueue',{
cache: false,
url: "ajax_get_emei_list.php",
dataType: "json",
success: function(jsonData){
//console.log(jsonData);
if (jsonData["error"] != "") {
console.log(jsonData["error"]);
} else {
delete jsonData["error"];
imeiList = Object.keys(jsonData);
mapGlobals.JSON_employeeList = jsonData;
var resultHTMLEmp = "";
$.each( jsonData, function( index, item ){
const id = item.id;
const userId = item.userId;
const depName = item.departmentName;
const depCode = item.departmentCode;
const depId = item.departmentId;
const imei = item.imei;
const name = item.name;
const initials = item.initials;
resultHTMLEmp += "<li id=\"emp_"+imei+"\" class=\"list-group-item d-flex justify-content-between align-items-center listItem dep_"+depId+" isOffline d-none\" style=\"font-size: 0.9rem !important;\">("+initials+") "+name+"<span class=\"list-right d-flex align-items-center\"><span id=\"empInfo_"+imei+"\" class=\"listIconInfo me-2\"><i class=\"fa-solid fa-circle-info\"></i></span><span class=\"badge bg-danger border border-light rounded-circle\" title=\""+offlineLegend+"\"> </span></span></li>";
});
// employees
$("#employeeList").html(resultHTMLEmp);
// Add click event to listIconInfo
$('#employeeList .listIconInfo').on('click', function () {
event.stopPropagation();
// Get the clicked icon's position
const icon = $(this);
// Customize modal content dynamically based on the clicked element
const employeeId = $(this).closest('li').attr('id');
const persID = employeeId.split("_");
const imei = persID[1];
const emp = mapGlobals.JSON_employeeList[imei];
const location = mapGlobals.JSON_locationList[imei];
const id = emp.id;
const userId = emp.userId;
const name = emp.name;
const initials = emp.initials;
const phone = emp.phone;
if (typeof location !== "undefined" && location !== null) {
var deviceId = location.id;
var lat = location.lat;
var lng = location.lng;
}
const addressId = 'addressTxt_'+imei;
var labelHTML = '<div class="row">'
+ '<div id="selectedEmp" class="col">'
+ '(' + initials + ') ' + name ;
if (phone != "" && phone != "null") {
labelHTML += ' '
+ '<i class="fa-light fa-phone"></i> '
+ phone ;
}
labelHTML += '</div>'
+ '</div>';
labelHTML += '<div class="row">'
+ '<div class="col">'
+ '<span id="'+addressId+'"></span>'
+ '</div>'
+ '</div>';
$('#empInfoModalLabel').html(labelHTML);
$('#empInfoModal-userId').val(userId);
$("#day-gpsContainer").html("");
$("#period-gpsContainer").html("");
if (typeof location !== "undefined" && location !== null) {
geoCodeLatLng(lat, lng, addressId);
$("#empInfoModal-deviceId").val(deviceId);
} else {
const errmsg = '<?php echo $locale_text["GPSMAP_unknown_location"] ?>';
$("#"+addressId).html(errmsg);
$("#empInfoModal-deviceId").val("");
}
$("#empInfoModal").draggable({
handle: ".modal-header",
scroll: false // Prevent draggable from affecting scrolling
});
const nodata = '<?php echo $locale_text["GPSMAP_noData"] ?>';
//TODO: get data
getJobs(userId , "#empTodayJobContainer");
// Reset
$("#hideRouteBtn").click();
$("#hideRouteBtn").addClass("d-none");
$("#showRouteBtn").removeClass("d-none");
// Prevent scroll from getting dragged along
$('#empInfoModal-body').on('mousedown touchstart', function (e) {
e.stopPropagation(); // Stop propagation to the draggable modal
});
// Show the modal
const modal = new bootstrap.Modal(document.getElementById('empInfoModal'), {
backdrop: false // Disable backdrop interference
});
modal.show();
//document.getElementById('offcanvasScrolling').classList.add('show');
});
}
}
});
}
//=============================================================================
// Get a JSON list of department
//=============================================================================
function getDepartmentList(){
$.ajaxq('getQueue',{
cache: false,
url: "ajax_get_department_list.php",
dataType: "json",
success: function(jsonData){
//console.log(jsonData);
if (jsonData["error"] != "") {
console.log(jsonData["error"]);
} else {
delete jsonData["error"];
//get departments
var resultHTMLDep = "";
$.each( jsonData, function( index, item ){
const id = item.id;
const name = item.name;
const userCount = item.userCount;
resultHTMLDep += "<option value=\""+id+"\">"+name+"</option>";
});
// departments
$("#depListDropDown").html(resultHTMLDep);
$("#depListDropDown").chosen({width: "100%"});
$("#jobDepListDropDown").html(resultHTMLDep);
$("#jobDepListDropDown").chosen({width: "100%"});
$("#jobCreateDepListDropDown").html("<option value=\"\"></option>" + resultHTMLDep);
$("#jobCreateDepListDropDown").chosen({width: "100%", allow_single_deselect: true});
}
}
});
}
//=============================================================================
// Get a JSON list of employees
//=============================================================================
function getEmployeeList(){
$.ajaxq('getQueue',{
cache: false,
url: "ajax_get_employee_list.php",
dataType: "json",
success: function(jsonData){
//console.log(jsonData);
if (jsonData["error"] != "") {
console.log(jsonData["error"]);
} else {
delete jsonData["error"];
var resultHTMLEmp = "";
$.each( jsonData, function( index, item ){
const id = item.id;
const userId = item.userId;
const depName = item.departmentName;
var depId = item.departmentId ;
const orgName = item.organizationName;
const orgId = item.organizationId ;
const imei = item.imei;
const name = item.name;
const initials = item.initials;
if (!depId || depId == "") depId = orgId;
resultHTMLEmp += "<option value=\""+userId+"\" class=\"dep_"+depId+" \">("+initials+") "+name+"</option>";
});
// employees
$("#jobEmpListDropDown").html(resultHTMLEmp);
$("#jobEmpListDropDown").chosen({width: "100%"});
$("#jobCreateEmpListDropDown").html("<option class=\"allDep\" value=\"\"></option>" + resultHTMLEmp);
$("#jobCreateEmpListDropDown").chosen({width: "100%"});
}
}
});
}
//=============================================================================
// Create job
//=============================================================================
function jobCreate(customer){
const id = customer.id;
const name = customer.name;
const label = '<?php echo $locale_text["GPSMAP_create_job"] ?>';
var labelHTML = label + " <span class=\"fw-bold\">" + name + "</span>";
const form = document.getElementById("jobCreateForm");
form.reset(); // Clear all inputs, selects, and textareas
$("#jobCreateDepListDropDown").val("").trigger("chosen:updated"); // Reset dropdown
$("#jobCreateActTypeListDropDown").val("").trigger("chosen:updated"); // Reset dropdown
$("#jobCreateEmpListDropDown").val("").trigger("chosen:updated"); // Reset dropdown
$('#jobCreateModalLabel').html(labelHTML);
$('#jobCreateModal-custId').val(id);
$('#jobCreateModal-serviceunitId').val('');
// Show the modal
const modal = new bootstrap.Modal(document.getElementById('jobCreateModal'), {
backdrop: false // Disable backdrop interference
});
modal.show();
}
//=============================================================================
// Create job serviceunit
//=============================================================================
function jobCreateServiceunit(serviceunitID, serviceunitName){
if(mapGlobals.selectedCustomer != ""){
jobCreate(mapGlobals.selectedCustomer, "CUST");
}
else{
alert("<?php echo $locale_text["ERROR_CHOOSE_CUST"]; ?>");
}
var customer = mapGlobals.selectedCustomer;
const id = customer.id;
const name = customer.name;
const label = '<?php echo $locale_text["GPSMAP_create_job"] ?>';
var labelHTML = label + " <span class=\"fw-bold\">" + name + " / " + serviceunitName + "</span>";
const form = document.getElementById("jobCreateForm");
form.reset(); // Clear all inputs, selects, and textareas
$("#jobCreateDepListDropDown").val("").trigger("chosen:updated"); // Reset dropdown
$("#jobCreateActTypeListDropDown").val("").trigger("chosen:updated"); // Reset dropdown
$("#jobCreateEmpListDropDown").val("").trigger("chosen:updated"); // Reset dropdown
$('#jobCreateModalLabel').html(labelHTML);
$('#jobCreateModal-custId').val(id);
$('#jobCreateModal-serviceunitId').val(serviceunitID);
// Show the modal
const modal = new bootstrap.Modal(document.getElementById('jobCreateModal'), {
backdrop: false // Disable backdrop interference
});
modal.show();
}
//=============================================================================
// Clear jobliste
//=============================================================================
function clearJoblist(){
// Hide all jobs
$("#hideAllJobsBtn").click();
$("#mainJobList").html("");
$("#clearAllJobsBtn").addClass("d-none");
$("#showAllJobsBtn").addClass("d-none");
}
//=============================================================================
// Get jobliste
//=============================================================================
function getJoblist(){
$("#job-message").addClass("d-none");
// Hide all jobs
$("#hideAllJobsBtn").click();
const fromDate = $('#job-fromdateInput').val();
const toDate = $('#job-todateInput').val();
const selectedDep = $('#jobDepListDropDown').val();
var selectedEmp = $('#jobEmpListDropDown').val();
const selectedStatus = $('#jobStatusListDropDown').val();
const selectedActType = $('#jobActTypeListDropDown').val();
const jobGetUnassigned = $('#jobGetUnassigned').prop('checked');
const getUnassigned = (jobGetUnassigned ? 'Y' : 'N');
if (selectedEmp.length == 0 && selectedDep.length > 0) {
// Filter out the disabled options
var allOptions = $('#jobEmpListDropDown option');
var nonDisabledOptions = allOptions.filter(function () {
return !$(this).is(':disabled');
});
let nonDisabledValues = nonDisabledOptions.map(function () {
return $(this).val();
}).get();
selectedEmp = nonDisabledValues;
}
if (fromDate.trim() == "") {
$("#job-message").removeClass("d-none").html( '<?php echo $locale_text["ERROR_FROMDATE_WARNING"] ?>' );
$('#job-fromdateInput').focus();
return;
}
if (toDate.trim() == "") {
$("job-message").removeClass("d-none").html( '<?php echo $locale_text["ERROR_TODATE_WARNING"] ?>' );
$('#job-todateInput').focus();
return;
}
if (toDate < fromDate) {
$("#job-message").removeClass("d-none").html( '<?php echo $locale_text["ERROR_DATE_GREATER"] ?>' );
$('#job-todateInput').focus();
return;
}
parmData = 'selectedDep='+selectedDep
+ '&selectedEmp='+selectedEmp
+ '&selectedStatus='+selectedStatus
+ '&selectedActType='+selectedActType
+ '&startDate=' + fromDate
+ '&endDate=' + toDate
+ '&getUnassigned=' + getUnassigned
+ '&type=**LIST**'
;
$("#jobListSpinner").removeClass("d-none");
$.ajax({
cache: false,
url: "ajax_get_joblist.php",
data: parmData,
dataType: "json",
success: function(jsonData){
//console.log(jsonData);
if (jsonData["error"] != "") {
console.log(jsonData["error"]);
} else {
delete jsonData["error"];
const jobList = jsonData["jobList"];
const jobscheduleList = jsonData["jobscheduleList"];
var resultCount = Object.keys(jobList).length;
if ( resultCount == 0 ) {
var resultHTML = "<?php echo $locale_text["GPSMAP_noData"]; ?>"
} else {
var resultHTML = "";
$("#showAllJobsBtn").removeClass("d-none");
$("#clearAllJobsBtn").removeClass("d-none");
}
$.each( jobList, function( index, item ){
empJobList[item["jobNumber"]] = item;
const id = item["id"];
const jobNumber = item["jobNumber"];
const externalJobNumber = item["externalJobNumber"];
const jobTagName = item["jobTagName"];
const activityTypeName = item["activityTypeName"];
const customerName = item["customerName"];
const responsibleName = item["responsibleName"];
const createdByName = item["createdByName"];
const isGroup = item["isGroup"];
const departmentName = item["departmentName"];
const customerCode = item["customerCode"];
const customerCategory = item["customerCategory"];
const serviceUnitCode = item["serviceUnitCode"];
const serviceUnitName = item["serviceUnitName"];
const serviceUnitSerialNumber = item["serviceUnitSerialNumber"];
const serviceUnitCategory = item["serviceUnitCategory"];
const assignedTo = item["assignedTo"];
const completedDateTime = item["completedDateTime"];
const totalScheduledWork = item["totalScheduledWork"];
const totalTimeLogged = item["totalTimeLogged"];
const clientDateTime = item["clientDateTime"];
const isRecurring = item["isRecurring"];
const isCritical = item["isCritical"];
const jobStatus = item["jobStatus"];
const contactPerson = item["contactPerson"];
const contactPhone = item["contactPhone"];
const contactEmail = item["contactEmail"];
const contactMobile = item["contactMobile"];
const shortDescription = item["shortDescription"];
const requisitionNumber = item["requisitionNumber"];
const estimatedHours = item["estimatedHours"];
const startDateTime = item["startDateTime"];
const startDateTimeLocal = item["startDateTimeLocal"];
const dueDateTime = item["dueDateTime"];
const dueDateTimeLocal = item["dueDateTimeLocal"];
const location = item["location"];
const street = item["street"];
const zipCode = item["zipCode"];
const city = item["city"];
const country = item["country"];
const longitude = item["longitude"];
const latitude = item["latitude"];
const createdAt = item["createdAt"];
const createdDateTime = item["createdDateTime"];
mapGlobals.JSON_jobList[jobNumber] = item;
var jobStatusTxt = jobStatusText[jobStatus];
var address = item["location"] + " " + item["street"] + " " + item["zipCode"] + " " + item["city"]+ " " + item["country"] ;
address = address.replace("null", "").trim();
mapGlobals.JSON_jobList[jobNumber]["address"] = address;
resultHTML += "<li id=\"job_"+jobNumber+"\" class=\"list-group-item listItem jobListItem text-break text-wrap\"><span class=\"d-flex justify-content-between align-items-center\">("+jobNumber+") "+shortDescription+"<span class=\"list-right d-flex align-items-center\"><span id=\"jobInfo_"+jobNumber+"\" class=\"listIconInfo jobSearchIcon me-2\"><i class=\"fa-solid fa-circle-info\"></i></span></span></span><span>"+address+"</span></li>";
});
$("#jobListSpinner").addClass("d-none");
$("#mainJobList").html(resultHTML);
// Add click event to listIconInfo
$("#mainJobList").on("mouseenter", ".listIconInfo", function () {
const $icon = $(this);
// Clear any existing timeout to avoid multiple triggers
clearTimeout(hoverTimeout);
// Check if the popover is already visible for the current icon
if ($icon.attr("aria-describedby")) {
return; // Popover is already shown; do nothing
}
// Dispose of any existing popover to prevent duplicates
$(".jobSearchIcon").not($icon).each(function() {
$(this).popover("dispose");
});
hoverTimeout = setTimeout(() => {
var jobID = $(this).attr("id").split("_");
jobID = jobID[1];
const jobUuid = empJobList[jobID]["id"]
parmData = 'jobUuid='+jobUuid
;
$.ajax({
cache: false,
url: "ajax_get_jobdetail.php",
data: parmData,
dataType: "json",
success: function(jsonDataDetail){
//console.log(jsonDataDetail);
if (jsonDataDetail["error"] != "") {
console.log(jsonDataDetail["error"]);
} else {
delete jsonDataDetail["error"];
const jobData = jsonDataDetail[0];
const jobNumber = jobData["jobNumber"];
const jobStatus = jobData["jobStatus"];
const customerName = jobData["customerName"];
const shortDescription = jobData["shortDescription"];
const longDescription = jobData["longDescription"];
const activityTypeName = jobData["activityTypeName"];
const serviceUnitName = jobData["serviceUnitName"];
const serviceUnitCode = empJobList[jobID]["serviceUnitCode"];
const location = jobData["location"];
const street = jobData["street"];
const zipCode = jobData["zipCode"];
const city = jobData["city"];
const country = jobData["country"];
const contactPerson = jobData["contactPerson"];
const contactEmail = jobData["contactEmail"];
const contactMobile = jobData["contactMobile"];
const contactPhone = jobData["contactPhone"];
const assignedTo = empJobList[jobID]["assignedTo"];
const startDateTimeLocal = jobData["startDateTimeLocal"];
const dueDateTimeLocal = jobData["dueDateTimeLocal"];
var estimatedHours = jobData["estimatedHours"];
if (estimatedHours) {
var hours = Math.floor(estimatedHours)
var decimal = estimatedHours - hours;
//check if minuttes
if (decimal > 0) {
var estimatedMin = Math.round(decimal*60) + "<?php echo $locale_text["GPSMAP_min_short"]; ?>";
} else {
var estimatedMin = "";
}
estimatedHours = hours + "<?php echo $locale_text["GPSMAP_hour_short"]; ?> " + estimatedMin;
} else {
estimatedHours = "-";
}
var customerAddr = (location ? location + ", " : "") + (street ? street + ", " : "") + (zipCode ? zipCode + " " : "") + (city ? city + ", " : "") + (country ? country : "");
var contactInfo = (contactEmail ? "<i class=\"fa-regular fa-envelope\"></i> " +contactEmail + ", " : "") + (contactMobile ? "<i class=\"fa-regular fa-phone\"></i> " + contactMobile + ", " : "") + (contactPhone ? "<i class=\"fa-regular fa-phone-office\"></i> " + contactPhone : "");
var resultHTML = '<div class="row">';
resultHTML += "<div class=\"col-3 fw-bold\"><?php echo $locale_text["GPSMAP_description"]; ?>:</div>";
resultHTML += "<div class=\"col-9\">"+longDescription+"</div>";
resultHTML += "</div>";
resultHTML += '<div class="row">';
resultHTML += "<div class=\"col-3 fw-bold\"><?php echo $locale_text["GPSMAP_act_type"]; ?>:</div>";
resultHTML += "<div class=\"col-9\">"+activityTypeName+"</div>";
resultHTML += "</div>";
resultHTML += '<div class="row">';
resultHTML += "<div class=\"col-3 fw-bold\"><?php echo $locale_text["GPSMAP_customer"]; ?>:</div>";
resultHTML += "<div class=\"col-9\">"+customerName+"</div>";
resultHTML += "</div>";
resultHTML += '<div class="row">';
resultHTML += "<div class=\"col-3 fw-bold\"></div>";
resultHTML += "<div class=\"col-9\">"+customerAddr+"</div>";
resultHTML += "</div>";
if (serviceUnitName) {
resultHTML += '<div class="row">';
resultHTML += "<div class=\"col-3 fw-bold\"><?php echo $locale_text["GPSMAP_serviceunit"]; ?>:</div>";
resultHTML += "<div class=\"col-9\">("+serviceUnitCode+") "+serviceUnitName+"</div>";
resultHTML += "</div>";
}
resultHTML += '<div class="row">';
resultHTML += "<div class=\"col-3 fw-bold\"><?php echo $locale_text["GPSMAP_contact_person"]; ?>:</div>";
resultHTML += "<div class=\"col-9\">"+contactPerson+"</div>";
resultHTML += "</div>";
resultHTML += '<div class="row">';
resultHTML += "<div class=\"col-3 fw-bold\"></div>";
resultHTML += "<div class=\"col-9\">"+contactInfo+"</div>";
resultHTML += "</div>";
resultHTML += '<div class="row">';
resultHTML += "<div class=\"col-3 fw-bold\"><?php echo $locale_text["GPSMAP_startdate"]; ?>:</div>";
resultHTML += "<div class=\"col-9\">"+startDateTimeLocal+"</div>";
resultHTML += "</div>";
resultHTML += '<div class="row">';
resultHTML += "<div class=\"col-3 fw-bold\"><?php echo $locale_text["GPSMAP_deadline"]; ?>:</div>";
resultHTML += "<div class=\"col-9\">"+dueDateTimeLocal+"</div>";
resultHTML += "</div>";
resultHTML += '<div class="row">';
resultHTML += "<div class=\"col-3 fw-bold\"><?php echo $locale_text["GPSMAP_duration"]; ?>:</div>";
resultHTML += "<div class=\"col-9\">"+estimatedHours+"</div>";
resultHTML += "</div>";
resultHTML += '<div class="row">';
resultHTML += "<div class=\"col-3 fw-bold\"><?php echo $locale_text["GPSMAP_employees"]; ?>:</div>";
resultHTML += "<div class=\"col-9\">"+assignedTo+"</div>";
resultHTML += "</div>";
// Initialize popover with the fetched content
$icon.popover({
title: "<div class=\"d-flex justify-content-between fw-bold\"><div>("+jobNumber+") " + shortDescription + "</div><div>"+jobStatusText[jobStatus]+"</div></div>",
trigger: "manual",
placement: "right",
html: true,
customClass: "wide-popover600",
content: resultHTML
});
// Show the popover
$icon.popover("show");
}
},
beforeSend: function() {
},
complete: function() {
},
error: function(xhr, status, error) {
console.log(error);
}
});
}, 300); // Delay in milliseconds
});
}
},
beforeSend: function() {
},
complete: function() {
},
error: function(xhr, status, error) {
console.log(error);
}
});
}
//=============================================================================
// Get jobstatus
//=============================================================================
function getJobstatus(){
var resultHTMLStatusDrop = "";
//resultHTMLStatusDrop += "<option value=\"0\">"+jobStatusText[0]+"</option>";
resultHTMLStatusDrop += "<option value=\"1\" selected=\"selected\">"+jobStatusText[1]+"</option>";
resultHTMLStatusDrop += "<option value=\"2\">"+jobStatusText[2]+"</option>";
resultHTMLStatusDrop += "<option value=\"3\">"+jobStatusText[3]+"</option>";
resultHTMLStatusDrop += "<option value=\"4\">"+jobStatusText[4]+"</option>";
resultHTMLStatusDrop += "<option value=\"5\">"+jobStatusText[5]+"</option>";
resultHTMLStatusDrop += "<option value=\"6\">"+jobStatusText[6]+"</option>";
resultHTMLStatusDrop += "<option value=\"7\">"+jobStatusText[7]+"</option>";
$("#jobStatusListDropDown").html(resultHTMLStatusDrop);
$("#jobStatusListDropDown").chosen({width: "100%"});
}
//=============================================================================
// Get serviceunits
//=============================================================================
function getServiceUnitList(refID) {
var customerId = mapGlobals.JSON_customerListID[refID];
parmData = 'CustomerId='+customerId
;
$.ajax({
cache: false,
url: "ajax_get_serviceunit_list.php",
dataType: "json",
data: parmData,
success: function(jsonData){
var resultHTMLserviceunits = "";
//console.log(jsonData);
if (jsonData["error"] != "") {
console.log(jsonData["error"]);
} else {
delete jsonData["error"];
$.each( jsonData, function( index, item ){
const id = item.id;
const relatedTechnicianName = item.relatedTechnicianName;
const customerName = item.customerName;
const remarks = item.remarks;
const active = item.active;
const contactName = item.contactName;
const contactEmail = item.contactEmail;
const contactPhone = item.contactPhone;
const contactMobile = item.contactMobile;
const name = item.name;
const code = item.code;
const serialNumber = item.serialNumber;
const qrCode = item.qrCode;
const country = item.country;
const city = item.city;
const region = item.region;
const street = item.street;
const location = item.location;
const zipCode = item.zipCode;
const attribute = item.attribute;
mapGlobals.JSON_serviceUnitID[code] = item;
if (active && index != 'error') {
resultHTMLserviceunits += "<div class=\"row mt-2\">";
resultHTMLserviceunits += "<div class=\"col listfilterRow d-flex justify-content-between align-items-center\" id=\"sunit_"+code+"\" ><span id=\"sunit_name_"+code+"\" >"+name+"</span> <span> <span id='customerJobcreateIcon' onclick='jobCreateServiceunit(\""+id+"\",\""+name+"\")' title='<?php echo $locale_text["GPSMAP_create_job"]; ?>' ><i class='fa-regular fa-calendar-circle-plus'></i> </span> <span id=\"sunit_name_info_"+code+"\" class='servUnitInfoIcon listIconInfo'><i class='fa-solid fa-circle-info'></i> </span> </span></div>";
resultHTMLserviceunits += "</div>";
}
});
$("#customerReferenceListContainer").html(resultHTMLserviceunits);
$("#customerReferenceListContainer").removeClass("d-none");
if (resultHTMLserviceunits != "") $("#referenceSearchWrapper").removeClass("d-none");
// Attach hover handler to .listIconInfo within the search results
$("#customerReferenceListContainer").on("mouseenter", ".listIconInfo", function () {
const $icon = $(this);
var divId = $icon.attr('id');
var divIdArr = divId.split("_");
var selectedServUnit = mapGlobals.JSON_serviceUnitID[divIdArr[3]];
var attributeTotal = Object.keys(selectedServUnit.attribute).length;
// Dispose of any existing popover to prevent duplicates
$(".servUnitInfoIcon").each(function() {
$(this).popover("dispose");
});
var borderRight = "";
var colsize = "col-6";
var popoverClass = "wide-popover300";
if ( attributeTotal > 0 ) {
borderRight = "border-end";
colsize = "col-3";
popoverClass = "wide-popover600";
}
//we uses the lines in the first column to show standard information, attributes are shown in second column, but number of lines depends on how many have data
var resultHTML = '<div class="row">';
resultHTML += '<div class="'+colsize+' fw-bold"><?php echo $locale_text["GPSMAP_number"]; ?></div>';
resultHTML += '<div class="'+colsize+' '+borderRight+'">' + selectedServUnit.code + '</div>';
if(attributeTotal > 0) {
resultHTML += '<div class="col-3 fw-bold">'+selectedServUnit.attribute[0]["name"]+'</div>';
resultHTML += '<div class="col-3">' + (selectedServUnit.attribute[0]["attributeValueName"] ? selectedServUnit.attribute[0]["attributeValueName"] : "") + '</div>';
} else {
resultHTML += '<div class="col-3 fw-bold"></div>';
resultHTML += '<div class="col-3"></div>';
}
resultHTML += "</div>";
resultHTML += '<div class="row">';
resultHTML += '<div class="'+colsize+' fw-bold"><?php echo $locale_text["GPSMAP_name"]; ?></div>';
resultHTML += '<div class="'+colsize+' '+borderRight+'">' + selectedServUnit.name + '</div>';
if(attributeTotal > 1) {
resultHTML += '<div class="col-3 fw-bold">'+selectedServUnit.attribute[1]["name"]+'</div>';
resultHTML += '<div class="col-3">' + (selectedServUnit.attribute[1]["attributeValueName"] ? selectedServUnit.attribute[1]["attributeValueName"] : "") + '</div>';
} else {
resultHTML += '<div class="col-3 fw-bold"></div>';
resultHTML += '<div class="col-3"></div>';
}
resultHTML += "</div>";
resultHTML += '<div class="row">';
resultHTML += '<div class="'+colsize+' fw-bold"><?php echo $locale_text["GPSMAP_category"]; ?></div>';
resultHTML += '<div class="'+colsize+' '+borderRight+'">' + selectedServUnit.categoryName + '</div>';
if(attributeTotal > 2) {
resultHTML += '<div class="col-3 fw-bold">'+selectedServUnit.attribute[2]["name"]+'</div>';
resultHTML += '<div class="col-3">' + (selectedServUnit.attribute[2]["attributeValueName"] ? selectedServUnit.attribute[2]["attributeValueName"] : "") + '</div>';
} else {
resultHTML += '<div class="col-3 fw-bold"></div>';
resultHTML += '<div class="col-3"></div>';
}
resultHTML += "</div>";
resultHTML += '<div class="row">';
resultHTML += '<div class="'+colsize+' fw-bold"><?php echo $locale_text["GPSMAP_QR"]; ?></div>';
resultHTML += '<div class="'+colsize+' '+borderRight+'">' + (selectedServUnit.qrCode ? selectedServUnit.qrCode : "") + '</div>';
if(attributeTotal > 3) {
resultHTML += '<div class="col-3 fw-bold">'+selectedServUnit.attribute[3]["name"]+'</div>';
resultHTML += '<div class="col-3">' + (selectedServUnit.attribute[3]["attributeValueName"] ? selectedServUnit.attribute[3]["attributeValueName"] : "") + '</div>';
} else {
resultHTML += '<div class="col-3 fw-bold"></div>';
resultHTML += '<div class="col-3"></div>';
}
resultHTML += "</div>";
resultHTML += '<div class="row">';
resultHTML += '<div class="'+colsize+' fw-bold"><?php echo $locale_text["GPSMAP_location"]; ?></div>';
resultHTML += '<div class="'+colsize+' '+borderRight+'">' + (selectedServUnit.location ? selectedServUnit.location : "") + '</div>';
if(attributeTotal > 4) {
resultHTML += '<div class="col-3 fw-bold">'+selectedServUnit.attribute[4]["name"]+'</div>';
resultHTML += '<div class="col-3">' + (selectedServUnit.attribute[4]["attributeValueName"] ? selectedServUnit.attribute[4]["attributeValueName"] : "") + '</div>';
} else {
resultHTML += '<div class="col-3 fw-bold"></div>';
resultHTML += '<div class="col-3"></div>';
}
resultHTML += "</div>";
resultHTML += '<div class="row">';
resultHTML += '<div class="'+colsize+' fw-bold"><?php echo $locale_text["GPSMAP_address"]; ?></div>';
resultHTML += '<div class="'+colsize+' '+borderRight+'">' + (selectedServUnit.street ? selectedServUnit.street : "") + '</div>';
if(attributeTotal > 5) {
resultHTML += '<div class="col-3 fw-bold">'+selectedServUnit.attribute[5]["name"]+'</div>';
resultHTML += '<div class="col-3">' + (selectedServUnit.attribute[5]["attributeValueName"] ? selectedServUnit.attribute[5]["attributeValueName"] : "") + '</div>';
} else {
resultHTML += '<div class="col-3 fw-bold"></div>';
resultHTML += '<div class="col-3"></div>';
}
resultHTML += "</div>";
resultHTML += '<div class="row">';
resultHTML += '<div class="'+colsize+' fw-bold"><?php echo $locale_text["GPSMAP_zip_city"]; ?></div>';
resultHTML += '<div class="'+colsize+' '+borderRight+'">' + (selectedServUnit.zipCode ? selectedServUnit.zipCode : "") + " " + (selectedServUnit.city ? selectedServUnit.city : "") + '</div>';
if(attributeTotal > 6) {
resultHTML += '<div class="col-3 fw-bold">'+selectedServUnit.attribute[6]["name"]+'</div>';
resultHTML += '<div class="col-3">' + (selectedServUnit.attribute[6]["attributeValueName"] ? selectedServUnit.attribute[6]["attributeValueName"] : "") + '</div>';
} else {
resultHTML += '<div class="col-3 fw-bold"></div>';
resultHTML += '<div class="col-3"></div>';
}
resultHTML += "</div>";
resultHTML += '<div class="row">';
resultHTML += '<div class="'+colsize+' fw-bold"><?php echo $locale_text["GPSMAP_country"]; ?></div>';
resultHTML += '<div class="'+colsize+' '+borderRight+'">' + (selectedServUnit.country ? selectedServUnit.country : "") + '</div>';
if(attributeTotal > 7) {
resultHTML += '<div class="col-3 fw-bold">'+selectedServUnit.attribute[7]["name"]+'</div>';
resultHTML += '<div class="col-3">' + (selectedServUnit.attribute[7]["attributeValueName"] ? selectedServUnit.attribute[7]["attributeValueName"] : "") + '</div>';
} else {
resultHTML += '<div class="col-3 fw-bold"></div>';
resultHTML += '<div class="col-3"></div>';
}
resultHTML += "</div>";
resultHTML += '<div class="row">';
resultHTML += '<div class="'+colsize+' fw-bold"><?php echo $locale_text["GPSMAP_contact_person"]; ?></div>';
resultHTML += '<div class="'+colsize+' '+borderRight+'">' + (selectedServUnit.contactName ? selectedServUnit.contactName : "") + '</div>';
if(attributeTotal > 8) {
resultHTML += '<div class="col-3 fw-bold">'+selectedServUnit.attribute[8]["name"]+'</div>';
resultHTML += '<div class="col-3">' + (selectedServUnit.attribute[8]["attributeValueName"] ? selectedServUnit.attribute[8]["attributeValueName"] : "") + '</div>';
} else {
resultHTML += '<div class="col-3 fw-bold"></div>';
resultHTML += '<div class="col-3"></div>';
}
resultHTML += "</div>";
resultHTML += '<div class="row">';
resultHTML += '<div class="'+colsize+' fw-bold"><?php echo $locale_text["GPSMAP_phone"]; ?></div>';
resultHTML += '<div class="'+colsize+' '+borderRight+'">' + (selectedServUnit.contactPhone ? selectedServUnit.contactPhone : "") + '</div>';
if(attributeTotal > 9) {
resultHTML += '<div class="col-3 fw-bold">'+selectedServUnit.attribute[9]["name"]+'</div>';
resultHTML += '<div class="col-3">' + (selectedServUnit.attribute[9]["attributeValueName"] ? selectedServUnit.attribute[9]["attributeValueName"] : "") + '</div>';
} else {
resultHTML += '<div class="col-3 fw-bold"></div>';
resultHTML += '<div class="col-3"></div>';
}
resultHTML += "</div>";
resultHTML += '<div class="row">';
resultHTML += '<div class="'+colsize+' fw-bold"><?php echo $locale_text["GPSMAP_cell_phone"]; ?></div>';
resultHTML += '<div class="'+colsize+' '+borderRight+'">' + (selectedServUnit.contactMobile ? selectedServUnit.contactMobile : "") + '</div>';
if(attributeTotal > 10) {
resultHTML += '<div class="col-3 fw-bold">'+selectedServUnit.attribute[10]["name"]+'</div>';
resultHTML += '<div class="col-3">' + (selectedServUnit.attribute[10]["attributeValueName"] ? selectedServUnit.attribute[9]["attributeValueName"] : "") + '</div>';
} else {
resultHTML += '<div class="col-3 fw-bold"></div>';
resultHTML += '<div class="col-3"></div>';
}
resultHTML += "</div>";
resultHTML += '<div class="row">';
resultHTML += '<div class="'+colsize+' fw-bold"><?php echo $locale_text["GPSMAP_email"]; ?></div>';
resultHTML += '<div class="'+colsize+' '+borderRight+'">' + (selectedServUnit.contactEmail ? selectedServUnit.contactEmail : "") + '</div>';
if(attributeTotal > 11) {
resultHTML += '<div class="col-3 fw-bold">'+selectedServUnit.attribute[11]["name"]+'</div>';
resultHTML += '<div class="col-3">' + (selectedServUnit.attribute[11]["attributeValueName"] ? selectedServUnit.attribute[9]["attributeValueName"] : "") + '</div>';
} else {
resultHTML += '<div class="col-3 fw-bold"></div>';
resultHTML += '<div class="col-3"></div>';
}
resultHTML += "</div>";
if(attributeTotal > 12) {
for(var i = 12; i < attributeTotal; i++) {
resultHTML += '<div class="row">';
resultHTML += '<div class="col-3 fw-bold"></div>';
resultHTML += '<div class="col-3 '+borderRight+'"></div>';
resultHTML += '<div class="col-3 fw-bold">'+selectedServUnit.attribute[i]["name"]+'</div>';
resultHTML += '<div class="col-3">' + (selectedServUnit.attribute[i]["attributeValueName"] ? selectedServUnit.attribute[i]["attributeValueName"] : "") + '</div>';
resultHTML += "</div>";
}
}
// Initialize popover with the fetched content
$icon.popover({
title: "<?php echo $locale_text["GPSMAP_info"] ?>",
trigger: "manual",
placement: "right",
html: true,
customClass: popoverClass,
content: resultHTML
});
// Attach the event listener for 'inserted.bs.popover' before showing the popover
$icon.on('inserted.bs.popover', function () {
// Bind click event to each .custResultLine element within the popover
$('.popover-body .custResultLine').on('click', function () {
fillInMachineInfo(this.id);
});
});
// Show the popover
$icon.popover("show");
});
}
},
beforeSend: function() {
},
complete: function() {
},
error: function(xhr, status, error) {
console.log(error);
}
});
}
//=============================================================================
// filter on customerReferenceList
//=============================================================================
function referenceFilter() {
var input = document.getElementById("refFilter");
var filter = input.value.toUpperCase();
var elementsArrayRow = document.getElementsByClassName("listfilterRow");
var filterHit = false;
for(var i=0; i<elementsArrayRow.length; i++) {
var id = elementsArrayRow[i].id;
var txtId = id.replace("sunit", "sunit_name");
var txtValue = document.getElementById(txtId).innerHTML.toUpperCase();
//check if any hit
if (txtValue.toUpperCase().indexOf(filter) > -1) {
filterHit = true;
//show listLabel
elementsArrayRow[i].classList.remove("d-none")
} else {
elementsArrayRow[i].classList.add("d-none")
}
}
if (!filterHit) {
$("#refFilterResult").show();
} else {
$("#refFilterResult").hide();
}
}
//=============================================================================
// Get activityTypes
//=============================================================================
function getActTypes(){
$.ajaxq('getQueue',{
cache: false,
url: "ajax_get_activitytype_list.php",
dataType: "json",
success: function(jsonData){
var resultHTMLActTypesDrop = "";
//console.log(jsonData);
if (jsonData["error"] != "") {
console.log(jsonData["error"]);
} else {
delete jsonData["error"];
$.each( jsonData, function( index, item ){
const id = item.id;
const name = item.name;
resultHTMLActTypesDrop += "<option value=\""+id+"\">"+ name +"</option>";
});
$("#jobActTypeListDropDown").html(resultHTMLActTypesDrop);
$("#jobActTypeListDropDown").chosen({width: "100%"});
$("#jobCreateActTypeListDropDown").html("<option value=\"\"></option>" + resultHTMLActTypesDrop);
$("#jobCreateActTypeListDropDown").chosen({width: "100%", allow_single_deselect: true});
}
},
beforeSend: function() {
},
complete: function() {
},
error: function(xhr, status, error) {
console.log(error);
}
});
}
//=============================================================================
// Get users Jobs
//=============================================================================
function getJobs(userId, container) {
if (container == "#empTodayJobContainer") {
var today = new Date();
var tomorrow = new Date(today);
tomorrow.setDate(tomorrow.getDate() + 1);
var startDate = today.getDate() + '-' + (today.getMonth() + 1) +'-' + today.getFullYear() + ' 00:00:00';
var endDate = tomorrow.getDate() + '-' + (tomorrow.getMonth() + 1) +'-' + tomorrow.getFullYear() + ' 00:00:00';
} else if (container == "#day-JobContainer") {
const selectedDate = $('#day-dateInput').val();
var nextDate = new Date(selectedDate);
var startDate = nextDate.getDate() + '-' + (nextDate.getMonth() + 1) +'-' + nextDate.getFullYear() + ' 00:00:00';
nextDate.setDate(nextDate.getDate() + 1);
var endDate = nextDate.getDate() + '-' + (nextDate.getMonth() + 1) +'-' + nextDate.getFullYear() + ' 00:00:00';
} else {
var startDate = "";
var endDate = "";
}
parmData = 'userId='+userId
+ '&startDate=' + startDate
+ '&endDate=' + endDate
+ '&type=**USER**'
;
$.ajax({
cache: false,
url: "ajax_get_joblist.php",
data: parmData,
dataType: "json",
success: function(jsonData){
//console.log(container);
//console.log(jsonData);
if (jsonData["error"] != "") {
console.log(jsonData["error"]);
} else {
delete jsonData["error"];
const jobList = jsonData["jobList"];
const jobscheduleList = jsonData["jobscheduleList"];
var resultCount = Object.keys(jobList).length;
if ( resultCount == 0 ) {
var resultHTML = "<?php echo $locale_text["GPSMAP_noData"]; ?>"
} else {
var resultHTML = "<div id=\"empTodayJobList\" class=\"overflow-y-auto overflow-x-hidden\" style=\"max-height: 150px;\">";
resultHTML += '<div class="row sticky-top border-bottom fw-bold bg-body-secondary opacity-100 bg-white">';
resultHTML += '<div class="col-2 text-break text-wrap"><?php echo $locale_text["GPSMAP_job"]; ?></div>';
resultHTML += '<div class="col-2 text-break text-wrap"><?php echo $locale_text["GPSMAP_customer"]; ?></div>';
resultHTML += '<div class="col-3 text-break text-wrap"><?php echo $locale_text["GPSMAP_address"]; ?></div>';
resultHTML += '<div class="col-3 text-break text-wrap"><?php echo $locale_text["GPSMAP_duration"]; ?></div>';
resultHTML += '<div class="col-2 text-break text-wrap"></div>';
resultHTML += "</div>";
}
$.each( jobList, function( index, item ){
empJobList[item["jobNumber"]] = item;
const id = item["id"];
const jobNumber = item["jobNumber"];
const externalJobNumber = item["externalJobNumber"];
const jobTagName = item["jobTagName"];
const activityTypeName = item["activityTypeName"];
const customerName = item["customerName"];
const responsibleName = item["responsibleName"];
const createdByName = item["createdByName"];
const isGroup = item["isGroup"];
const departmentName = item["departmentName"];
const customerCode = item["customerCode"];
const customerCategory = item["customerCategory"];
const serviceUnitCode = item["serviceUnitCode"];
const serviceUnitName = item["serviceUnitName"];
const serviceUnitSerialNumber = item["serviceUnitSerialNumber"];
const serviceUnitCategory = item["serviceUnitCategory"];
const assignedTo = item["assignedTo"];
const completedDateTime = item["completedDateTime"];
const totalScheduledWork = item["totalScheduledWork"];
const totalTimeLogged = item["totalTimeLogged"];
const clientDateTime = item["clientDateTime"];
const isRecurring = item["isRecurring"];
const isCritical = item["isCritical"];
const jobStatus = item["jobStatus"];
const contactPerson = item["contactPerson"];
const contactPhone = item["contactPhone"];
const contactEmail = item["contactEmail"];
const contactMobile = item["contactMobile"];
const shortDescription = item["shortDescription"];
const requisitionNumber = item["requisitionNumber"];
const estimatedHours = item["estimatedHours"];
const startDateTime = item["startDateTime"];
const startDateTimeLocal = item["startDateTimeLocal"];
const dueDateTime = item["dueDateTime"];
const dueDateTimeLocal = item["dueDateTimeLocal"];
const location = item["location"];
const street = item["street"];
const zipCode = item["zipCode"];
const city = item["city"];
const country = item["country"];
const createdAt = item["createdAt"];
const createdDateTime = item["createdDateTime"];
var jobStatusTxt = jobStatusText[jobStatus];
var address = item["location"] + " " + item["street"] + " " + item["zipCode"] + " " + item["city"]+ " " + item["country"] ;
address = address.replace("null", "").trim();
var jobschedules = jobscheduleList[jobNumber];
var scheduleHTML = "";
$.each( jobschedules, function( index2, schedule ){
const startDateTimeLocalSchedule = schedule["startDateTimeLocal"];
const durationSchedule = schedule["duration"];;
var durationFormatted = getFormattedMinutes(durationSchedule*60);
var newline = "<br>";
if (scheduleHTML == "") newline = "";
scheduleHTML += newline + "" + startDateTimeLocalSchedule + " (" + durationFormatted + ")" ;
});
resultHTML += '<div class="row border-bottom">';
resultHTML += '<div class="col-2 text-break text-wrap">('+jobNumber+') '+shortDescription+'</div>';
resultHTML += '<div class="col-2 text-break text-wrap">'+customerName+'</div>';
resultHTML += '<div class="col-3 text-break text-wrap">'+address+'</div>';
resultHTML += '<div class="col-3 text-break text-wrap">'+scheduleHTML+'</div>';
resultHTML += '<div id="jobIcons_'+jobNumber+'" class="col-2 text-break text-wrap"><span class=\"jobSearchIcon listIconInfo me-2\"><i class=\"fa-solid fa-circle-info\"></i></span>';
if (container == "#day-JobContainer") {
resultHTML += '  <input type="checkbox" class="historyMenuPrint printJob" id="printJob_'+id+'" name="printJob_'+id+'" value="yes" title="<?php echo $locale_text["GPSMAP_print"]; ?>">';
}
resultHTML += "</div>";
resultHTML += "</div>";
});
$(container).html(resultHTML);
// Add click event to listIconInfo
$(container).on("mouseenter", ".listIconInfo", function () {
const $icon = $(this);
// Dispose of any existing popover to prevent duplicates
$(".jobSearchIcon").each(function() {
$(this).popover("dispose");
});
var divId = $icon.parent().attr('id');
var divIdArr = divId.split("_");
const id = divIdArr[1];
const jobUuid = empJobList[id]["id"]
parmData = 'jobUuid='+jobUuid
;
$.ajax({
cache: false,
url: "ajax_get_jobdetail.php",
data: parmData,
dataType: "json",
success: function(jsonDataDetail){
//console.log(jsonDataDetail);
if (jsonDataDetail["error"] != "") {
console.log(jsonDataDetail["error"]);
} else {
delete jsonDataDetail["error"];
const jobData = jsonDataDetail[0];
const jobNumber = jobData["jobNumber"];
const jobStatus = jobData["jobStatus"];
const customerName = jobData["customerName"];
const shortDescription = jobData["shortDescription"];
const longDescription = jobData["longDescription"];
const activityTypeName = jobData["activityTypeName"];
const serviceUnitName = jobData["serviceUnitName"];
const serviceUnitCode = empJobList[id]["serviceUnitCode"];
const location = jobData["location"];
const street = jobData["street"];
const zipCode = jobData["zipCode"];
const city = jobData["city"];
const country = jobData["country"];
const contactPerson = jobData["contactPerson"];
const contactEmail = jobData["contactEmail"];
const contactMobile = jobData["contactMobile"];
const contactPhone = jobData["contactPhone"];
const assignedTo = empJobList[id]["assignedTo"];
const startDateTimeLocal = jobData["startDateTimeLocal"];
const dueDateTimeLocal = jobData["dueDateTimeLocal"];
var estimatedHours = jobData["estimatedHours"];
if (estimatedHours) {
var hours = Math.floor(estimatedHours)
var decimal = estimatedHours - hours;
//check if minuttes
if (decimal > 0) {
var estimatedMin = Math.round(decimal*60) + "<?php echo $locale_text["GPSMAP_min_short"]; ?>";
} else {
var estimatedMin = "";
}
estimatedHours = hours + "<?php echo $locale_text["GPSMAP_hour_short"]; ?> " + estimatedMin;
} else {
estimatedHours = "-";
}
var customerAddr = (location ? location + ", " : "") + (street ? street + ", " : "") + (zipCode ? zipCode + " " : "") + (city ? city + ", " : "") + (country ? country : "");
var contactInfo = (contactEmail ? "<i class=\"fa-regular fa-envelope\"></i> " +contactEmail + ", " : "") + (contactMobile ? "<i class=\"fa-regular fa-phone\"></i> " + contactMobile + ", " : "") + (contactPhone ? "<i class=\"fa-regular fa-phone-office\"></i> " + contactPhone : "");
var resultHTML = '<div class="row">';
resultHTML += "<div class=\"col-3 fw-bold\"><?php echo $locale_text["GPSMAP_description"]; ?>:</div>";
resultHTML += "<div class=\"col-9\">"+longDescription+"</div>";
resultHTML += "</div>";
resultHTML += '<div class="row">';
resultHTML += "<div class=\"col-3 fw-bold\"><?php echo $locale_text["GPSMAP_act_type"]; ?>:</div>";
resultHTML += "<div class=\"col-9\">"+activityTypeName+"</div>";
resultHTML += "</div>";
resultHTML += '<div class="row">';
resultHTML += "<div class=\"col-3 fw-bold\"><?php echo $locale_text["GPSMAP_customer"]; ?>:</div>";
resultHTML += "<div class=\"col-9\">"+customerName+"</div>";
resultHTML += "</div>";
resultHTML += '<div class="row">';
resultHTML += "<div class=\"col-3 fw-bold\"></div>";
resultHTML += "<div class=\"col-9\">"+customerAddr+"</div>";
resultHTML += "</div>";
if (serviceUnitName) {
resultHTML += '<div class="row">';
resultHTML += "<div class=\"col-3 fw-bold\"><?php echo $locale_text["GPSMAP_serviceunit"]; ?>:</div>";
resultHTML += "<div class=\"col-9\">("+serviceUnitCode+") "+serviceUnitName+"</div>";
resultHTML += "</div>";
}
resultHTML += '<div class="row">';
resultHTML += "<div class=\"col-3 fw-bold\"><?php echo $locale_text["GPSMAP_contact_person"]; ?>:</div>";
resultHTML += "<div class=\"col-9\">"+contactPerson+"</div>";
resultHTML += "</div>";
resultHTML += '<div class="row">';
resultHTML += "<div class=\"col-3 fw-bold\"></div>";
resultHTML += "<div class=\"col-9\">"+contactInfo+"</div>";
resultHTML += "</div>";
resultHTML += '<div class="row">';
resultHTML += "<div class=\"col-3 fw-bold\"><?php echo $locale_text["GPSMAP_startdate"]; ?>:</div>";
resultHTML += "<div class=\"col-9\">"+startDateTimeLocal+"</div>";
resultHTML += "</div>";
resultHTML += '<div class="row">';
resultHTML += "<div class=\"col-3 fw-bold\"><?php echo $locale_text["GPSMAP_deadline"]; ?>:</div>";
resultHTML += "<div class=\"col-9\">"+dueDateTimeLocal+"</div>";
resultHTML += "</div>";
resultHTML += '<div class="row">';
resultHTML += "<div class=\"col-3 fw-bold\"><?php echo $locale_text["GPSMAP_duration"]; ?>:</div>";
resultHTML += "<div class=\"col-9\">"+estimatedHours+"</div>";
resultHTML += "</div>";
resultHTML += '<div class="row">';
resultHTML += "<div class=\"col-3 fw-bold\"><?php echo $locale_text["GPSMAP_employees"]; ?>:</div>";
resultHTML += "<div class=\"col-9\">"+assignedTo+"</div>";
resultHTML += "</div>";
// Initialize popover with the fetched content
$icon.popover({
title: "<div class=\"d-flex justify-content-between fw-bold\"><div>("+jobNumber+") " + shortDescription + "</div><div>"+jobStatusText[jobStatus]+"</div></div>",
trigger: "manual",
placement: "right",
html: true,
customClass: "wide-popover600",
content: resultHTML
});
// Attach the event listener for 'inserted.bs.popover' before showing the popover
$icon.on('inserted.bs.popover', function () {
// Bind click event to each .custResultLine element within the popover
$('.popover-body .custResultLine').on('click', function () {
fillInMachineInfo(this.id);
});
});
// Show the popover
$icon.popover("show");
}
},
beforeSend: function() {
},
complete: function() {
},
error: function(xhr, status, error) {
console.log(error);
}
});
});
}
},
beforeSend: function() {
},
complete: function() {
},
error: function(xhr, status, error) {
console.log(error);
}
});
}
//=============================================================================
// Get timelogs
//=============================================================================
function getTimelogs(userId){
const selectedDate = $('#day-dateInput').val();
var nextDate = new Date(selectedDate);
var startDate = nextDate.getDate() + '-' + (nextDate.getMonth() + 1) +'-' + nextDate.getFullYear() + ' 00:00:00';
nextDate.setDate(nextDate.getDate() + 1);
var endDate = nextDate.getDate() + '-' + (nextDate.getMonth() + 1) +'-' + nextDate.getFullYear() + ' 23:59:59';
parmData = 'userId='+userId
+ '&startDate=' + startDate
+ '&endDate=' + endDate
;
$.ajax({
cache: false,
url: "ajax_get_timelog.php",
data: parmData,
dataType: "json",
success: function(jsonData){
//console.log(jsonData);
if (jsonData["error"] != "") {
console.log(jsonData["error"]);
} else {
delete jsonData["error"];
var resultCount = Object.keys(jsonData).length;
if ( resultCount == 0 ) {
var resultHTML = "<?php echo $locale_text["GPSMAP_noData"]; ?>"
} else {
var resultHTML = "<div id=\"day-timesheetContainer-data\" class=\"overflow-y-auto overflow-x-hidden\" style=\"max-height: 150px;\">";
resultHTML += '<div class="row sticky-top border-bottom fw-bold bg-body-secondary opacity-100 bg-white">';
resultHTML += '<div class="col-2 text-break text-wrap"><?php echo $locale_text["GPSMAP_job"]; ?></div>';
resultHTML += '<div class="col-2 text-break text-wrap"><?php echo $locale_text["GPSMAP_customer"]; ?></div>';
resultHTML += '<div class="col-3 text-break text-wrap"><?php echo $locale_text["GPSMAP_address"]; ?></div>';
resultHTML += '<div class="col-2 text-break text-wrap"><?php echo $locale_text["GPSMAP_salary_type"]; ?></div>';
resultHTML += '<div class="col-2 text-break text-wrap"><?php echo $locale_text["GPSMAP_duration"]; ?></div>';
resultHTML += '<div class="col-1 text-break text-wrap"></div>';
resultHTML += "</div>";
$.each( jsonData, function( index, item ){
const id = item["id"];
const jobNumber = item["jobNumber"];
const jobName = item["jobName"];
const customerName = item["customerName"];
var address = item["location"] + " " + item["street"] + " " + item["zipCode"] + " " + item["city"]+ " " + item["country"] ;
address = address.replace("null", "").trim();
const salaryTypeName = item["salaryTypeName"];
const statusName = item["statusName"];
const sorttype = item["sorttype"];
const unit = item["unit"];
const unitValue = item["unitValue"];
var hours = "";
if (!unit) {
const EndTimeLocal = item[sorttype+"EndTimeLocal"];
const StartTimeLocal = item[sorttype+"StartTimeLocal"];
const endStamp = Math.round( Date.parse( EndTimeLocal ) / 1000);
const startStamp = Math.round( Date.parse( StartTimeLocal ) / 1000);
var EndTimeLocalArr = EndTimeLocal.split(" ");
var EndHourLocalArr = EndTimeLocalArr[1].split(":");
var StartTimeLocalArr = StartTimeLocal.split(" ");
var StartHourLocalArr = StartTimeLocalArr[1].split(":");
hours = "<br>("+StartHourLocalArr[0]+":"+StartHourLocalArr[1]+ " - " + EndHourLocalArr[0]+":"+EndHourLocalArr[1] + ")";
var diff = timeDifference(endStamp, startStamp);
var duration = getFormattedMinutes(diff);
} else {
var duration = unitValue + " " + unit;
}
resultHTML += '<div class="row border-bottom">';
resultHTML += '<div class="col-2 text-break text-wrap">('+jobNumber+') '+jobName+'</div>';
resultHTML += '<div class="col-2 text-break text-wrap">'+customerName+'</div>';
resultHTML += '<div class="col-3 text-break text-wrap">'+address+'</div>';
resultHTML += '<div class="col-2 text-break text-wrap">'+salaryTypeName+' ('+statusName+')</div>';
resultHTML += '<div class="col-2 text-break text-wrap">'+duration+hours+'</div>';
resultHTML += '<div class="col-1 text-break text-wrap"><input type="checkbox" class="historyMenuPrint printScheme" id="printScheme_'+id+'" name="printScheme_'+id+'" value="yes" title="<?php echo $locale_text["GPSMAP_print"]; ?>"></div>';
resultHTML += "</div>";
});
resultHTML += "</div>";
}
$("#day-timesheetContainer").html(resultHTML);
}
},
beforeSend: function() {
},
complete: function() {
},
error: function(xhr, status, error) {
console.log(error);
}
});
}
//=============================================================================
// Get days data
//=============================================================================
function getDayData(){
getRoute('*DAY*');
var userId = $("#empInfoModal-userId").val();
getJobs(userId, "#day-JobContainer");
getTimelogs(userId);
}
//=============================================================================
// Get Route
//=============================================================================
function getRoute(type){
var parmData = "";
$("#modal-message").addClass("d-none");
$("#hideRouteBtn").click();
$("#printGPSTransactionsAllCheck").prop('checked', false); // Unchecks it
if (type == '*DAY*') {
const selectedDate = $('#day-dateInput').val();
const deviceId =$("#empInfoModal-deviceId").val();
if (selectedDate.trim() == "") {
$("#modal-message").removeClass("d-none").html( '<?php echo $locale_text["ERROR_DATE_WARNING"] ?>' );
$('#day-dateInput').focus();
return;
}
parmData = 'from_date=' + selectedDate
+ '&to_date=' + selectedDate
+ '&device_id=' + deviceId
;
} else if (type == '*PERIOD*') {
const fromDate = $('#period-fromdateInput').val();
const toDate = $('#period-todateInput').val();
const deviceId =$("#empInfoModal-deviceId").val();
if (fromDate.trim() == "") {
$("#modal-message").removeClass("d-none").html( '<?php echo $locale_text["ERROR_FROMDATE_WARNING"] ?>' );
$('#period-fromdateInput').focus();
return;
}
if (toDate.trim() == "") {
$("#modal-message").removeClass("d-none").html( '<?php echo $locale_text["ERROR_TODATE_WARNING"] ?>' );
$('#period-todateInput').focus();
return;
}
if (toDate < fromDate) {
$("#modal-message").removeClass("d-none").html( '<?php echo $locale_text["ERROR_DATE_GREATER"] ?>' );
$('#period-todateInput').focus();
return;
}
const date1 = new Date(fromDate);
const date2 = new Date(toDate);
const diffTime = Math.abs(date2 - date1);
const diffDays = Math.floor(diffTime / (1000 * 60 * 60 * 24));
if (diffDays > 31) {
$("#modal-message").removeClass("d-none").html( '<?php echo $locale_text["ERROR_PERIOD"] ?>' );
$('#period-todateInput').focus();
return;
}
parmData = 'from_date=' + fromDate
+ '&to_date=' + toDate
+ '&device_id=' + deviceId
;
}
excelKeys = {};
$.ajaxq('historyQueue',{
url: 'gps_tracker_get_history.php',
type: 'GET',
data: parmData,
dataType: 'json',
success: function(jsonData) {
if (jsonData["error"] != "") {
console.log(jsonData["error"]);
}
mapGlobals.JSON_gpsTransactions = jsonData.data;
var routeArray = new Array(); // holds route info
var totalDistance = 0;
var totalDuration = 0;
var curDistance = 0;
var prevTrans = {};
var curTrans = {};
var lastMoveTrans = {};
var firstPauseTrans = {};
var currentRoute = {};
var moving = 'N';
var shortstop = 'N';
excelData = [];
excelKeys = {};
for(i in mapGlobals.JSON_gpsTransactions) {
curTrans = mapGlobals.JSON_gpsTransactions[i];
// Calculate route distance
totalDistance += curTrans["distance"];
curDistance += curTrans["distance"];
// speed is used to decide if we are on the move or not
// a route is from A to B ignoring stops under 5 min
if (curTrans["speed"] > 0) {
if (moving == 'N') {
currentRoute["startTime"] = curTrans["time"];
currentRoute["startTimeStamp"] = curTrans["timestamp"]; //stamp in seconds
currentRoute["startLat"] = curTrans["latitude"];
currentRoute["startLon"] = curTrans["longitude"];
currentRoute["driving"] = 'Y';
moving = 'Y';
}
shortstop = 'N';
lastMoveTrans = curTrans;
} else {
if (moving == 'Y') {
if (timeDifference(curTrans["timestamp"],lastMoveTrans["timestamp"]) >= 300) { // More than 5 min
moving = 'N';
if (shortstop == 'Y') {
currentRoute["endTime"] = firstPauseTrans["time"];
currentRoute["endTimeStamp"] = firstPauseTrans["timestamp"];
currentRoute["endLat"] = firstPauseTrans["latitude"];
currentRoute["endLon"] = firstPauseTrans["longitude"];
currentRoute["distance"] = curDistance;
currentRoute["duration"] = timeDifference(firstPauseTrans["timestamp"],currentRoute["startTimeStamp"]);
} else {
currentRoute["endTime"] = curTrans["time"];
currentRoute["endTimeStamp"] = curTrans["timestamp"];
currentRoute["endLat"] = curTrans["latitude"];
currentRoute["endLon"] = curTrans["longitude"];
currentRoute["distance"] = curDistance;
currentRoute["duration"] = timeDifference(curTrans["timestamp"],currentRoute["startTimeStamp"]);
}
currentRoute["driving"] = 'N';
routeArray.push(currentRoute);
totalDuration += currentRoute["duration"];
//Reset
currentRoute = {};
curDistance = 0;
shortstop = 'N';
} else if (shortstop == 'N') {
shortstop = 'Y';
firstPauseTrans = curTrans;
}
}
}
prevTrans = curTrans;
}
if (!jQuery.isEmptyObject(currentRoute)) {
if (shortstop == 'Y') {
currentRoute["endTime"] = firstPauseTrans["time"];
currentRoute["endTimeStamp"] = firstPauseTrans["timestamp"];
currentRoute["endLat"] = firstPauseTrans["latitude"];
currentRoute["endLon"] = firstPauseTrans["longitude"];
currentRoute["distance"] = curDistance;
currentRoute["duration"] = timeDifference(firstPauseTrans["timestamp"],currentRoute["startTimeStamp"]);
currentRoute["driving"] = 'N';
} else {
currentRoute["endTime"] = curTrans["time"];
currentRoute["endTimeStamp"] = curTrans["timestamp"];
currentRoute["endLat"] = curTrans["latitude"];
currentRoute["endLon"] = curTrans["longitude"];
currentRoute["distance"] = curDistance;
currentRoute["duration"] = timeDifference(curTrans["timestamp"],currentRoute["startTimeStamp"]);
currentRoute["driving"] = 'Y';
}
routeArray.push(currentRoute);
totalDuration += currentRoute["duration"];
}
mapGlobals.routeArray = routeArray;
// Set html
var html = "";
if(routeArray.length > 0){ // If result
$("#getrouteBtnDay").addClass("col-disabled");
$("#getrouteBtnPeriod").addClass("col-disabled");
$("#printCol").addClass("col-disabled");
$("#printExcelCol").addClass("col-disabled");
$("#printPeriodCol").addClass("col-disabled");
var excelHeader = {};
excelHeader["start"] = "<?php echo $locale_text["GPSMAP_start"]; ?>";
excelHeader["startLocation"] = "";
excelHeader["stop"] = "<?php echo $locale_text["GPSMAP_stop"]; ?>";
excelHeader["stopLocation"] = "";
excelHeader["distance"] = "<?php echo $locale_text["GPSMAP_distance"]; ?>";
excelHeader["duration"] = "<?php echo $locale_text["GPSMAP_duration"]; ?>";
excelHeader["durationRaw"] = "<?php echo $locale_text["GPSMAP_duration"]; ?>";
excelData.push(excelHeader);
html += "<div class=\"border\">";
if (type == "*PERIOD*") {
html += "<div id=\"period-gpsContainer-data\" class=\"overflow-y-auto overflow-x-hidden\" style=\"max-height: 300px;\">";
} else {
html += "<div id=\"day-gpsContainer-data\" class=\"overflow-y-auto overflow-x-hidden\" style=\"max-height: 150px;\">";
}
//header
html += "<div class=\"row bg-body-secondary sticky-top border-bottom fw-bold opacity-100 bg-white\">";
html += "<div class=\"col-4 text-break text-wrap\"><?php echo $locale_text["GPSMAP_start"]; ?></div>";
html += "<div class=\"col-4 text-break text-wrap\"><?php echo $locale_text["GPSMAP_stop"]; ?></div>";
html += "<div class=\"col-2 text-break text-wrap\"><?php echo $locale_text["GPSMAP_distance"]; ?></div>";
html += "<div class=\"col-2 text-break text-wrap\"><?php echo $locale_text["GPSMAP_duration"]; ?></div>";
html += "</div>";
for(i in routeArray){
if (type == "*PERIOD*") {
var rowId = "routePeriod_" + i;
var routeStartId = "routeStartPeriod_" + i;
var routeEndId = "routeEndPeriod_" + i;
var printRouteChkBox = "";
} else {
var rowId = "route_" + i;
var routeStartId = "routeStart_" + i;
var routeEndId = "routeEnd_" + i;
var printRouteChkBox = "<input type=\"checkbox\" class=\"historyMenuPrint inSummation printRoute\" id=\"printRoute_"+ i +"\" name=\"printRoute_"+ i +"\" onclick='recalculateSummations();' value=\"yes\" title=\"<?php echo $locale_text["GPSMAP_print"]; ?>\">";
}
if (routeArray[i]["driving"] == 'Y') {
var truckIcon = '<span title="<?php echo $locale_text["GPSMAP_still_driving"]; ?>"><i class="fa-sharp fa-thin fa-truck-fast"></i></span>';
} else {
var truckIcon = '';
}
// row - stopped
if (i != 0) {
var stopDuration = timeDifference(routeArray[i-1]["endTimeStamp"],routeArray[i]["startTimeStamp"]);
html += "<div class=\"row bg-body-secondary border-bottom\">";
html += "<div class=\"col-4 text-break text-wrap\"><?php echo $locale_text["GPSMAP_stopped"]; ?></div>";
html += "<div class=\"col-4 text-break text-wrap\"></div>";
html += "<div class=\"col-2 text-break text-wrap\"></div>";
if (type == '*DAY*') {
html += "<div class=\"col-2 text-break text-wrap\">";
html += "<div class=\"row gx-0\">";
html += "<div class=\"col-7 text-break text-wrap\">"+getFormattedMinutes(stopDuration)+"</div>";
html += "<div class=\"col-5 text-break text-wrap text-end\">";
html += "<span id=\"hideRoute_"+ i +"\" onclick=\"hideRoutePart("+i+")\" class=\"listIcon listIconHideRoute cursorPointer invisible\" title=\"<?php echo $locale_text["GPSMAP_hideRoute"]; ?>\" ><i class=\"fad fa-route fa-fw\"></i></span>";
html += " <input type=\"checkbox\" class=\"historyMenuPrint printRoute\" id=\"printStop_" + i + "\" name=\"printStop_" + i + "\" value=\"yes\" title=\"<?php echo $locale_text["GPSMAP_print"]; ?>\">  ";
html += "</div>";
html += "</div>";
html += "</div>";
} else {
html += "<div class=\"col-2 text-break text-wrap\">"+getFormattedMinutes(stopDuration)+"</div>";
}
html += "</div>";
var excelRow = {};
excelRow["start"] = "<?php echo $locale_text["GPSMAP_stopped"]; ?>";
excelRow["startLocation"] = "";
excelRow["stop"] = "";
excelRow["stopLocation"] = "";
excelRow["distance"] = "";
excelRow["duration"] = getFormattedMinutes(stopDuration);
excelRow["durationRaw"] = stopDuration;
excelData.push(excelRow);
}
const routeStartTimeArr = routeArray[i]["startTime"].split(" ");
const routeEndTimeArr = routeArray[i]["endTime"].split(" ");
const routeStartTimeFormatted = routeStartTimeArr[0] + "<br> " + routeStartTimeArr[1];
const routeEndTimeFormatted = routeEndTimeArr[0] + "<br> " + routeEndTimeArr[1];
// row - driving
html += "<div class=\"row border-bottom\">";
html += "<div class=\"col-4 text-break text-wrap\">";
html += "<div class=\"row gx-0\">";
html += "<div class=\"col-5 text-break text-wrap\">"+routeStartTimeFormatted+"</div>";
html += "<div class=\"col-7 text-break text-wrap\"><div id=\""+ routeStartId +"\" class=\"spinner-border spinner-border-sm word-wrap\" aria-hidden=\"true\"></div></div>";
html += "</div>";
html += "</div>";
html += "<div class=\"col-4 text-break text-wrap\">";
html += "<div class=\"row gx-0\">";
html += "<div class=\"col-5 text-break text-wrap\">"+routeEndTimeFormatted+"</div>";
html += "<div class=\"col-7 text-break text-wrap\"><div id=\""+ routeEndId +"\" class=\"spinner-border spinner-border-sm word-wrap\" aria-hidden=\"true\"></div></div>";
html += "</div>";
html += "</div>";
html += "<div class=\"col-2 text-break text-wrap\">"+routeArray[i]["distance"].toFixed(2)+ " " + truckIcon + "<input id=\"distance_" + i + "\" class=\"d-none\" value=\"" + routeArray[i]["distance"] + "\" /></div>";
if (type == '*DAY*') {
html += "<div class=\"col-2 text-break text-wrap\">";
html += "<div class=\"row gx-0\">";
html += "<div class=\"col-7 text-break text-wrap\">"+getFormattedMinutes(routeArray[i]["duration"]) + "<input id=\"duration_" + i + "\" class=\"d-none\" value=\"" + routeArray[i]["duration"] + "\" /></div>";
html += "<div class=\"col-5 text-break text-wrap text-end\">";
html += "<span id=\"showRoute_"+ i +"\" onclick=\"showRoutePart("+i+")\" class=\"listIcon listIconShowRoute cursorPointer\" title=\"<?php echo $locale_text["GPSMAP_showRoute"]; ?>\" ><i class=\"fad fa-route fa-fw\" style=\"color: red;\"></i></span>";
html += "<span id=\"hideRoute_"+ i +"\" onclick=\"hideRoutePart("+i+")\" class=\"listIcon listIconHideRoute cursorPointer d-none\" title=\"<?php echo $locale_text["GPSMAP_hideRoute"]; ?>\" ><i class=\"fad fa-route fa-fw\"></i></span>";
html += " "+printRouteChkBox+"  ";
html += "</div>";
html += "</div>";
html += "</div>";
} else {
html += "<div class=\"col-2 text-break text-wrap\">"+getFormattedMinutes(routeArray[i]["duration"]) + "<input id=\"duration_" + i + "\" class=\"d-none\" value=\"" + routeArray[i]["duration"] + "\" /></div>";
}
html += "</div>";
var excelRow = {};
excelRow["start"] = routeArray[i]["startTime"];
excelRow["startLocation"] = "";
excelRow["stop"] = routeArray[i]["endTime"];
excelRow["stopLocation"] = "";
excelRow["distance"] = routeArray[i]["distance"].toFixed(2);
excelRow["duration"] = getFormattedMinutes(routeArray[i]["duration"]);
excelRow["durationRaw"] = routeArray[i]["duration"];
excelData.push(excelRow);
excelKeys[i] = excelData.length - 1; // Map id to index
}
var totalDurationTmp = getFormattedMinutes( (totalDuration / 1000) / 60);
var totalDurationLabel = "";
if(getFormattedMinutes( (totalDuration / 1000) / 60).hour > 0){
totalDurationLabel += getFormattedMinutes( (totalDuration / 1000) / 60).hour + " <?php echo $locale_text["GPSMAP_hour_short"]; ?> ";
}
if(getFormattedMinutes( (totalDuration / 1000) / 60).min > 0){
totalDurationLabel += getFormattedMinutes( (totalDuration / 1000) / 60).min + " <?php echo $locale_text["GPSMAP_min_short"]; ?>";
}
//totals
html += "<div class=\"row bg-body-secondary fw-bold sticky-bottom\">";;
html += "<div class=\"col-4 text-break text-wrap\"><?php echo $locale_text["GPSMAP_total"] . " (" . $locale_text["GPSMAP_driving_time"] . ")"; ?></div>";
html += "<div class=\"col-4 text-break text-wrap\"></div>";
html += "<div id=\"totalDistanceSummation\" class=\"col-2 text-break text-wrap\">"+totalDistance.toFixed(2)+" <?php echo $locale_text["GPSMAP_distance"]; ?></div>";
html += "<div id=\"totalDurationSummation\" class=\"col-2 text-break text-wrap;\">"+getFormattedMinutes(totalDuration)+"</div>";
html += "</div>";
$("#totalDistanceSummation_hidden").val(totalDistance.toFixed(2)+" <?php echo $locale_text["GPSMAP_distance"]; ?>");
$("#totalDurationSummation_hidden").val(getFormattedMinutes(totalDuration));
var excelTotal = {};
excelTotal["start"] = "<?php echo $locale_text["GPSMAP_total"] . " (" . $locale_text["GPSMAP_driving_time"] . ")"; ?>";
excelTotal["startLocation"] = "";
excelTotal["stop"] = "";
excelTotal["stopLocation"] = "";
excelTotal["distance"] = totalDistance.toFixed(2)+" <?php echo $locale_text["GPSMAP_distance"]; ?>";
excelTotal["duration"] = getFormattedMinutes(totalDuration);
excelTotal["durationRaw"] = totalDuration;
excelData.push(excelTotal);
html += "</div>"; // div with max-height
html += "</div>"; // div with border
} else {
html += "<div><?php echo $locale_text["GPSMAP_noData"]; ?></div>";
}
if (type == '*DAY*') {
$("#day-gpsContainer").html(html);
} else {
$("#period-gpsContainer").html(html);
}
let geocodePromises = [];
if(routeArray.length > 0){
// Reverse geo code to get address's, dones after HTML is set because of async reverse geo coding.
for(i in routeArray){
if (type == '*DAY*') {
var startAddressId = 'routeStart_'+i;
var endAddressId = 'routeEnd_'+i;
} else {
var startAddressId = 'routeStartPeriod_'+i;
var endAddressId = 'routeEndPeriod_'+i;
}
const startLat = routeArray[i]["startLat"]
const endLat = routeArray[i]["endLat"]
const startLon = routeArray[i]["startLon"]
const endLon = routeArray[i]["endLon"]
geocodePromises.push(geoCodeLatLng(startLat, startLon, startAddressId));
geocodePromises.push(geoCodeLatLng(endLat, endLon, endAddressId));
}
}
// Wait for all geocoding promises to resolve before print or excel
Promise.all(geocodePromises).then(() => {
// Enable the column by removing the disabled class
$("#printCol").removeClass("col-disabled"); // Enable the column
$("#printExcelCol").removeClass("col-disabled"); // Enable the column
$("#printPeriodCol").removeClass("col-disabled"); // Enable the column
$("#getrouteBtnDay").removeClass("col-disabled"); // Enable button
$("#getrouteBtnPeriod").removeClass("col-disabled"); // Enable button
}).catch((error) => {
console.error("Error with geocoding:", error);
});
},
beforeSend: function() {
$("#modal-message").removeClass("d-none").html( '<?php echo $locale_text["GPSMAP_rev_geo_in_progress"] ?>' );
},
complete: function() {
$("#modal-message").addClass("d-none").html( '' );
},
error: function(xhr, status, error) {
console.log(error);
}
});
}
//=============================================================================
// Get car locations.
//=============================================================================
function getCarlocations(mode){
var parmData = 'time=' + lastUpdated
+ '&mode=' + mode
;
$.ajaxq('getQueue',{
url: 'gps_tracker_get_devices_latest.php',
type: 'GET',
data: parmData,
dataType: 'json',
success: function(jsonData) {
//console.log(jsonData);
if (jsonData["error"] != "") {
console.log(jsonData["error"]);
} else {
lastUpdated = jsonData["time"];
}
$.each( jsonData.data, function( index, item ){
//console.log(item);
var lat = item.lat;
var lng = item.lng;
var device_data = item.device_data;
var imei = device_data.imei;
var course = device_data.course;
var online = item.online;
var speed = item.speed;
var status = 0; // red, offline (no data in 24h)
var statusColor = 'bg-danger';
var titleText = offlineLegend;
if (online != "offline") {
if (speed <= 5) {
status = 1; // yellow, paused
statusColor = 'bg-warning';
titleText = yellowLegend;
} else {
status = 2; //green, driving
statusColor = 'bg-success';
titleText = onlineLegend;
}
}
var n = 1;
if (imeiList.includes(imei)) {
mapGlobals.JSON_locationList[imei] = item;
carData[imei] = [];
var latLng = new google.maps.LatLng(lat, lng);
var markerData = new MarkerData("employee", latLng, "", "em_" + imei, imei, status, lat, lng, course);
n++;
carData[imei]["marker"] = markerData;
if ($('#employeeShowOffline').prop('checked') || status != 0) {
addMarker(markerData);
$("#emp_" + imei).addClass("listItemOn");
}
// Changes employee status
updateStatusColor(imei, statusColor, titleText);
}
});
},
error: function(xhr, status, error) {
console.log(error);
}
});
}
//=============================================================================
// update employee status Color
//=============================================================================
function updateStatusColor(imei, newClass, titleText) {
const empId = "emp_"+imei;
const badge = document.querySelector(`#${empId} .badge`);
if (badge) {
// Remove existing background color classes
badge.classList.remove('bg-danger', 'bg-success', 'bg-warning');
// Add the new background color class
badge.classList.add(newClass);
// Set the title attribute
if (titleText) {
badge.setAttribute('title', titleText);
}
}
if (newClass == "bg-danger") {
$("#"+empId).addClass("isOffline");
if ($('#employeeShowOffline').prop('checked')) $("#"+empId).removeClass("d-none");
else $("#"+empId).addClass("d-none");
} else {
$("#"+empId).removeClass("isOffline");
$("#"+empId).removeClass("d-none");
}
}
//=============================================================================
// add Marker to the map
//=============================================================================
function addMarker(markerData){
if(!markerData){
return false;
}
let marker = mapGlobals.allMarkers[markerData.id]; // Get the marker by ID
// General marker settings
if (marker) {
// Update existing marker
//console.log("update")
marker.setPosition(markerData.latLng);
marker.address = markerData.address; // Custom property
marker.taskStatus = markerData.taskStatus; // Custom property
marker.taskColor = markerData.taskColor; // Custom property
} else {
// Create a new marker
//console.log("new")
marker = new google.maps.Marker({
id: markerData.id,
position: markerData.latLng,
map: mapGlobals.map,
address: markerData.address,
draggable: false,
taskStatus: markerData.taskStatus || "",
taskColor: markerData.taskColor || ""
});
// Add the new marker to the global array
mapGlobals.allMarkers[markerData.id] = marker;
}
// Specific marker settings
switch(markerData.type){
case "customer":
marker.html = fillInfoWindow(markerData);
mapGlobals.currentCustomerMarker = marker;
setMapCenter(markerData.latLng);
marker.setIcon("images/marker/marker_customer1.png");
marker.setTitle("<?php echo $locale_text["GPSMAP_customer"]; ?>");
break;
case "address":
mapGlobals.addressMarker = marker;
setMapCenter(markerData.latLng);
marker.setIcon("images/marker/marker_address.png");
break;
case "employee":
var persID = markerData.id.split("_");
var empId = persID[1];
var init = "";
try {
var init = mapGlobals.JSON_employeeList[empId].initials;
}
catch(err) {
return;
}
var empName = mapGlobals.JSON_employeeList[empId].name;
var mobilePhoneNumber = mapGlobals.JSON_employeeList[empId].mobilePhone;
var course = markerData.course;
var bearing = getDirection(course);
marker.setIcon("include/marker_generator.php?text=" + init + "&type=" + markerData.online + "&bearing=" + bearing);
checkID = "em_" + persID[1];
for (var i = 0; i < mapGlobals.markerCluster.markers_.length; i++) {
if (checkID == mapGlobals.markerCluster.markers_[i].id){
mapGlobals.markerCluster.removeMarker(mapGlobals.markerCluster.markers_[i]);
}
}
mapGlobals.gpsEmployeeMarkersArray.push(marker);
mapGlobals.markerCluster.addMarker(marker);
break;
case "location":
marker.setIcon("images/marker/localmarker.png");
mapGlobals.gpsEmpLocationMarkers[markerData.id] = marker;
break;
case "home":
marker.html = fillInfoWindow(markerData);
mapGlobals.homeMarker = marker;
marker.setIcon("images/marker/marker_home.png");
marker.setTitle("<?php echo $locale_text["GPSMAP_legendHome"]; ?>");
setMapCenter(markerData.latLng); // added to get map showing
break;
case "department":
marker.html = fillInfoWindow(markerData);
marker.setIcon("images/marker/marker_department.png");
marker.setTitle(markerData.description);
break;
case "machine":
marker.html = fillInfoWindow(markerData);
marker.setIcon("images/marker/marker_machines.png");
marker.setTitle(markerData.description);
setMapCenter(markerData.latLng);
mapGlobals.selectedMachines[markerData.id] = marker;
break;
case "job":
switch(markerData.taskStatus){
case 0: // NotAssigned
color = "<?php echo $DFT_COLOR_JOB_NOT_ASSIGNED; ?>";
colorCode = color.substring(1);
taskIcon = "images/marker/marker_job_"+colorCode+".png";
break;
case 1: // Active
color = "<?php echo $DFT_COLOR_JOB_ACTIVE; ?>";
colorCode = color.substring(1);
taskIcon = "images/marker/marker_job_"+colorCode+".png";
break;
case 2: // InProgress
color = "<?php echo $DFT_COLOR_JOB_START; ?>";
colorCode = color.substring(1);
taskIcon = "images/marker/marker_job_"+colorCode+".png";
break;
case 3: // OnHold
color = "<?php echo $DFT_COLOR_JOB_ONHOLD; ?>";
colorCode = color.substring(1);
taskIcon = "images/marker/marker_job_"+colorCode+".png";
break;
case 4: // Review
color = "<?php echo $DFT_COLOR_JOB_REVIEW; ?>";
colorCode = color.substring(1);
taskIcon = "images/marker/marker_job_"+colorCode+".png";
break;
case 5: // Completed
color = "<?php echo $DFT_COLOR_JOB_CLOSED; ?>";
colorCode = color.substring(1);
taskIcon = "images/marker/marker_job_"+colorCode+".png";
break;
case 6: // DFT_COLOR_JOB_INVOICED
color = "<?php echo $DFT_COLOR_JOB_PARTLY_DONE; ?>";
colorCode = color.substring(1);
taskIcon = "images/marker/marker_job_"+colorCode+".png";
break;
case 7: // Cancelled
color = "<?php echo $DFT_COLOR_JOB_CANCEL; ?>";
colorCode = color.substring(1);
taskIcon = "images/marker/marker_job_"+colorCode+".png";
break;
default:
color = "<?php echo $DFT_COLOR_JOB_DEFAULT; ?>";
colorCode = color.substring(1);
taskIcon = "images/marker/marker_job_"+colorCode+".png";
break;
}
mapGlobals.selectedJobs[markerData.id] = marker;
//marker.setIcon("images/marker/marker_job.png");
marker.setTitle(markerData.description);
marker.setIcon(taskIcon);
marker.setZIndex(100000);
marker.taskStatus = markerData.taskStatus;
marker.taskColor = colorCode;
marker.taskPlannedperson = markerData.taskPlannedperson;
//setMapCenter(markerData.latLng);
mapGlobals.markerCluster.addMarker(marker);
break;
case "jobCustomer":
marker.setIcon("images/marker/marker_cust.png");
setMapCenter(markerData.latLng);
mapGlobals.jobInfoCustMarker = marker;
break;
case "jobDelivery":
marker.setIcon("images/marker/marker_del.png");
setMapCenter(markerData.latLng);
mapGlobals.jobInfoDelMarker = marker;
break;
}
marker.addListener('click', function(m) {
var markertype = markerData.id.split("_");
if(markertype[0] == "task") {
//getJobEdit(markertype[1], m);
if(mapGlobals.userLevel != 1) {
// Planner / admins can change all jobs
javascript:PopWin("../../details.php?taskid="+markertype[1]+"&personid=&referencecode=&status=0",720,1000, m);
} else {
// Regular user may only change own job.
if(mapGlobals.loggedInUser == markerData.taskPlannedperson || mapGlobals.loggedInUser == 0) {
javascript:PopWin("../../details.php?taskid="+markertype[1]+"&personid=&referencecode=&status=0",720,1000, m);
} else {
$javascript:alert('<?php echo $locale_text["ERROR_EDIT_NOT_ALLOWED"]?>');
}
}
}
});
marker.addListener('mouseover', function(m) {
var markertype = markerData.id.split("_");
var container = $(document.getElementById("mapContainer"));
if (!container.find('#tooltip').length)
container.append(jobtooltip);
if(markertype[0] == "task") {
projection = overlay.getProjection();
var pixel = projection.fromLatLngToContainerPixel(marker.getPosition());
toolTipTimer = setTimeout(function() { showJobInfoPopup(markertype[1], pixel.x, pixel.y); }, 400);
}
});
marker.addListener('mouseout', function(m) {
var markertype = markerData.id.split("_");
if(markertype[0] == "task") {
clearTimeout(toolTipTimer);
hideJobInfoPopup();
}
});
// Job marker have no info window so far.
if(markerData.type != "job"){
/* Closing infowindow on the cross, apparently google maps v3 has a bug and the event is fired twice.
* No problem here, but if a more complex function is called later on considere it and take measures.
*/
google.maps.event.addListener(mapGlobals.infoWin, "closeclick", function(){
if(mapGlobals.empInfoWinShown){
mapGlobals.empInfoWinShown = false;
}
});
google.maps.event.addListener(marker, "click", function () { // Register click event on marker and set info window content
if(markerData.type == "address"){
geoCodeCooridinates(this);
}
else if(markerData.type == "employee"){
mapGlobals.selectedEmployee = persID[1];
setMapCenter(markerData.latLng);
$("#gpsSummationHeader, #gpsSummationContainer").addClass("hidden");
var empObj = {
empId : mapGlobals.selectedEmployee,
empName : empName,
mobilePhoneNumber : mobilePhoneNumber
}
//console.log("emp id " + mapGlobals.selectedEmployee);
//showEmpInfo(empObj);
$("#empInfo_"+mapGlobals.selectedEmployee).click();
}
else{
mapGlobals.infoWin.setContent(this.html);
mapGlobals.infoWin.open(mapGlobals.map, this);
if(markerData.type == "employee"){
mapGlobals.empInfoWinShown = true;
mapGlobals.empInfoWinID = markerData.id;
var empID = markerData.id.split("_");
mapGlobals.selectedEmployee = empID[1];
}
else{
mapGlobals.empInfoWinShown = false;
}
}
});
}
}
//=============================================================================
// Remove a specific marker
//=============================================================================
function removeMarker(marker){
if(marker != null){
const markerId = marker.id;
marker.setMap(null);
// Update the allMarkers array by removing the marker
delete mapGlobals.allMarkers[markerId];
}
}
//=============================================================================
//Remove an employee marker
//=============================================================================
function removeEmployeeMarker(personID){
for(i in mapGlobals.gpsEmployeeMarkersArray){
if(mapGlobals.gpsEmployeeMarkersArray[i].id == "em_" + personID){
removeMarker(mapGlobals.gpsEmployeeMarkersArray[i]);
mapGlobals.markerCluster.removeMarker(mapGlobals.gpsEmployeeMarkersArray[i]);
}
}
}
//=============================================================================
// Get address from coordinates
//=============================================================================
function geoCodeCooridinates(marker){
var address = "";
if (marker.address != "") {
address = marker.address;
mapGlobals.infoWin.setContent("<div class='infoBoxWin boldText'>" + address + "</div>");
mapGlobals.infoWin.open(mapGlobals.map, marker);
} else {
var geocoder = new google.maps.Geocoder();
geocoder.geocode({'latLng': marker.position}, function(results, status) {
if(status == google.maps.GeocoderStatus.OK){
if(results[0]){
address = results[0].formatted_address;
mapGlobals.infoWin.setContent("<div class='infoBoxWin boldText'>" + address + "</div>");
mapGlobals.infoWin.open(mapGlobals.map, marker);
}
}
else{
//alert("Adress retrieval failed due to: " + status);
}
// Remove marker when close on infowin is clicked
google.maps.event.addListener(mapGlobals.infoWin,'closeclick',function(){
marker.setMap(null);
});
});
}
}
//=============================================================================
// Update Job with coordinates
//=============================================================================
function updateCoordinates(thisJobUuid, longitude, latitude){
var parmData = 'lat=' + latitude
+ '&lng=' + longitude
+ '&uuid=' + thisJobUuid
;
$.ajax({
url: 'ajax_update_job.php',
type: 'GET',
data: parmData,
dataType: 'json',
success: function(jsonData) {
//console.log(jsonData);
},
error: function(xhr, status, error) {
console.log(error);
}
});
}
//=============================================================================
// Get address from coordinates
//=============================================================================
function geoCodeLatLng(lat, lng, addressId){
// use promise to keep track on when geocoding are done
return new Promise((resolve, reject) => {
var parmData = 'lat=' + lat
+ '&lng=' + lng
;
$.ajaxq('getQueue',{
url: 'gps_tracker_address_reverse.php',
type: 'GET',
data: parmData,
dataType: 'json',
success: function(jsonData) {
if (jsonData["error"] != "") {
console.log(jsonData["error"]);
const errmsg = '<?php echo $locale_text["GPSMAP_unknown_location"] ?>';
$("#"+addressId).removeClass("spinner-border").removeClass("spinner-border-sm");
$("#"+addressId).html(errmsg);
} else {
const location = jsonData["data"]["location"];
const houseNo = (location["house"] != null ? location["house"] : "")
var addText = (location["road"] != null ? location["road"] + " " + houseNo + ", <br> " : "");
addText += ((location["city"] != null || location["zip"] != null) ? location["zip"] + " " + location["city"] : " ");
if (addText == "") addText = '<?php echo $locale_text["GPSMAP_unknown_location"] ?>';
$("#"+addressId).removeClass("spinner-border").removeClass("spinner-border-sm");
$("#"+addressId).html(addText);
//update excel
var addTextExcel = result = addText.replace("<br>", "");
var idArr = addressId.split("_");
if (idArr[0] == "routeStart" || idArr[0] == "routeStartPeriod") {
if (excelKeys[idArr[1]] !== undefined) {
excelData[excelKeys[idArr[1]]].startLocation = addTextExcel;
}
} else if (idArr[0] == "routeEnd" || idArr[0] == "routeEndPeriod") {
if (excelKeys[idArr[1]] !== undefined) {
excelData[excelKeys[idArr[1]]].stopLocation = addTextExcel;
}
}
}
resolve(); // Resolve the promise once the AJAX call is complete
},
error: function(xhr, status, error) {
console.log(error);
reject(error); // Reject the promise if there's an error
}
});
});
}
//=============================================================================
// Get coordinates for address
//=============================================================================
function geoCodeAddress(markerData, showResultList){
if(showResultList == undefined){
showResultList = false;
}
if ((markerData.latitude != undefined && markerData.longitude != undefined) && (markerData.latitude != "" && markerData.longitude != "") ){
var lat = markerData.latitude;
var lng = markerData.longitude;
var latlng = new google.maps.LatLng(lat, lng);
markerData.latLng = latlng;
addMarker(markerData);
return latlng;
} else {
var geocoder = new google.maps.Geocoder();
geocoder.geocode({'address' : markerData.address}, function(results, status){
if (status == google.maps.GeocoderStatus.OK){
if (markerData.type == "job") {
const latitude = results[0].geometry.location.lat();
const longitude = results[0].geometry.location.lng();
var jobIdArr = markerData.id.split("_");
const jobId = jobIdArr[1]
const thisJob = mapGlobals.JSON_jobList[jobId];
const thisJobUuid = thisJob.id;
mapGlobals.JSON_jobList[jobId]["latitude"] = latitude;
mapGlobals.JSON_jobList[jobId]["longitude"] = longitude;
updateCoordinates(thisJobUuid, longitude, latitude);
}
if(showResultList){
var addressSearchResult = $("#addressSearchResult");
addressSearchResult.hide();
addressSearchResult.empty();
if(results.length > 1){ // More than 1 result.
const $button = $("#addressSearchBtn");
// Dispose of the existing popover to reset its content
$button.popover("dispose");
var resultLi = "<ul class='searchResultList list-group' style='cursor: pointer;'>";
for(i in results){
resultLi += "<li class='custResultLink list-group-item' id='"+ results[i].geometry.location.lat() + "_" + results[i].geometry.location.lng() +"'>" + results[i].formatted_address + "</li>";
}
resultLi += "</ul>";
$button.popover({
trigger: "manual",
placement: "right",
html: true,
content: resultLi
});
// Attach the event listener for 'inserted.bs.popover' before showing the popover
$button.on('inserted.bs.popover', function () {
// Bind click event to each .custResultLine element within the popover
$('.popover-body .custResultLink').off('click').on('click', function () {
var thisId = this.id;
var idArr = thisId.split("_");
var latlng = new google.maps.LatLng(idArr[0], idArr[1]);
var address = $(this).text();
var markerData = new MarkerData("address", latlng, address, "addressMarker", null, null, null);
addMarker(markerData);
$("#addressSearchBtn").popover("hide");
});
});
$button.popover("show");
}
else{ // Only 1 result.
var lat = results[0].geometry.location.lat();
var lng = results[0].geometry.location.lng();
var latlng = new google.maps.LatLng(lat, lng);
markerData.latLng = latlng;
markerData.address = results[0].formatted_address;
addMarker(markerData);
}
}
else{
var lat = results[0].geometry.location.lat();
var lng = results[0].geometry.location.lng();
var latlng = new google.maps.LatLng(lat, lng);
markerData.latLng = latlng;
markerData.address = results[0].formatted_address;
addMarker(markerData);
return latlng;
}
}
else{
//alert("Geocode was not successful for the following reason: " + status);
alert(markerData.address + "\n<?php echo $locale_text["GPSMAP_noAddress"]; ?>");
if(markerData.type == "job"){
$("#" + markerData.id).removeClass("listItemOn");
}
return false;
}
});
}
}
//=============================================================================
// Calculate the time difference between two stamps.
//=============================================================================
function timeDifference(stamp1, stamp2){
return Math.abs(stamp1-stamp2);
}
//=============================================================================
// Format sec as hours and min
//=============================================================================
function getFormattedMinutes(totalSeconds){
var hours = Math.floor(totalSeconds / 3600);
totalSeconds = totalSeconds % 3600;
var minutes = Math.floor(totalSeconds / 60);
var seconds = totalSeconds % 60;
var formattedTime = (hours > 0 ? hours + " <?php echo $locale_text["GPSMAP_hour_short"]; ?> " : "");
formattedTime += (minutes > 0 ? minutes + " <?php echo $locale_text["GPSMAP_min_short"]; ?> " : "");
if (formattedTime == "") formattedTime = "<1 <?php echo $locale_text["GPSMAP_min_short"]; ?>";
return formattedTime;
}
//=============================================================================
// Show employees path on map. Draw a polyline. Print marker according to interval
//=============================================================================
function showRoute(){
var lastCoord = new google.maps.LatLng();
var gpsEmpPathPolyline = new Array();
for(i in mapGlobals.JSON_gpsTransactions){
var tempLatLng = new google.maps.LatLng(mapGlobals.JSON_gpsTransactions[i].latitude, mapGlobals.JSON_gpsTransactions[i].longitude);
// For polyline
if(tempLatLng.lat() != 0 && tempLatLng.lng() != 0){ // 0,0 coords not show on line
if(tempLatLng.lat() != lastCoord.lat() && tempLatLng.lng() != lastCoord.lng()){
gpsEmpPathPolyline.push(tempLatLng);
}
}
lastCoord = tempLatLng;
}
mapGlobals.routePolyline = drawPolyline(gpsEmpPathPolyline);
setMapCenter(tempLatLng);
mapGlobals.map.setZoom(11);
}
//=============================================================================
// Show a specific route
//=============================================================================
function showRoutePart(id){
$("#hideRouteBtn").trigger("click");
removePolyline(mapGlobals.routePolyline);
var startStamp = mapGlobals.routeArray[id]["startTimeStamp"];
var stopStamp = mapGlobals.routeArray[id]["endTimeStamp"];
var latLngList = new Array();
var routeStarted = false;
for(i in mapGlobals.JSON_gpsTransactions){
// Transactions without coordinate is skipped
if(!mapGlobals.JSON_gpsTransactions[i].latitude || !mapGlobals.JSON_gpsTransactions[i].longitude){
continue;
}
if(mapGlobals.JSON_gpsTransactions[i].timestamp == startStamp){
routeStarted = true;
}
if(routeStarted){
var latLng = new google.maps.LatLng(mapGlobals.JSON_gpsTransactions[i].latitude, mapGlobals.JSON_gpsTransactions[i].longitude);
latLngList.push(latLng);
}
if(mapGlobals.JSON_gpsTransactions[i].timestamp == stopStamp){
routeStarted = false;
break;
}
}
mapGlobals.routePolyline = drawPolyline(latLngList);
setMapCenter(latLng);
mapGlobals.map.setZoom(11);
$("#showRoute_"+ id).addClass("d-none");
$("#hideRoute_"+ id).removeClass("d-none");
}
//=============================================================================
// Hide a specific route
//=============================================================================
function hideRoutePart(id){
removePolyline(mapGlobals.routePolyline);
$("#showRoute_"+ id).removeClass("d-none");
$("#hideRoute_"+ id).addClass("d-none");
}
//=============================================================================
// Draw a polyline between cordinates
//=============================================================================
function drawPolyline(latLngList){
var polyline = new google.maps.Polyline({
path: latLngList,
strokeColor: "#FF3D64",
strokeOpacity: 1.0,
strokeWeight: 3
});
polyline.setMap(mapGlobals.map);
google.maps.event.addListener(polyline, 'click', function(event) {
removeMarker(mapGlobals.clickedPolyLineMarker);
var latLng = new google.maps.LatLng(event.latLng.lat(), event.latLng.lng());
mapGlobals.clickedPolyLineMarker = new google.maps.Marker({
id: "clickedPolyline",
position: latLng,
icon: "",
map: mapGlobals.map,
draggable: false
});
geoCodeCooridinates(mapGlobals.clickedPolyLineMarker)
});
return polyline;
}
//=============================================================================
// Removes polyline from map
//=============================================================================
function removePolyline(polyline){
if(polyline != undefined){
polyline.setMap(null);
}
}
//=============================================================================
// Recalculate Summations
//=============================================================================
function recalculateSummations(){
var totalDistance = 0;
var totalDuration = 0;
var hasChecked = false;
var elementsArray = document.getElementsByClassName("inSummation");
for(var i=0; i<elementsArray.length; i++) {
var id = elementsArray[i].id;
var idArr = id.split("_");
var checked = elementsArray[i].checked;
if (checked) {
hasChecked = true;
var thisDistance = document.getElementById("distance_"+idArr[1]).value;
var thisDuration = document.getElementById("duration_"+idArr[1]).value;
totalDistance += parseFloat(thisDistance.replace(',', '.'));
totalDuration += parseInt(thisDuration);
} else {
$("#printGPSTransactionsAllCheck").prop('checked', false); // Unchecks it
}
}
if (hasChecked) {
$("#totalDistanceSummation").html(totalDistance.toFixed(1) + " km");
$("#totalDurationSummation").html(getFormattedMinutes(totalDuration));
} else {
$("#totalDistanceSummation").html($("#totalDistanceSummation_hidden").val());
$("#totalDurationSummation").html($("#totalDurationSummation_hidden").val());
}
}
//=============================================================================
//Remove all markers from a given list
//=============================================================================
function removeAllMarkers(markerList){
for(i in markerList){
markerList[i].setMap(null);
}
}
//=============================================================================
// Center map on a latlng
//=============================================================================
function setMapCenter(latlng){
mapGlobals.map.setCenter(latlng); // Instantly to location
}
//=============================================================================
// Search address
//=============================================================================
function addressSearch(){
var address = $("#addressSearchVal").val();
if(address != ""){
removeMarker(mapGlobals.addressMarker);
var markerData = new MarkerData("address", null, address, "addressMarker", null, null, null);
geoCodeAddress(markerData, true);
}
else{
$("#addressSearchResult").hide();
}
}
//=============================================================================
// Search clear
//=============================================================================
function addressClear(){
$("#addressSearchVal").val("");
removeMarker(mapGlobals.addressMarker);
$("#addressSearchResult").hide();
}
//=============================================================================
// toggle sidebar
//=============================================================================
function toggleSidebar() {
const sidebar = document.getElementById("sidebar");
const toggleBtn = document.getElementById("toggle-btn");
sidebar.classList.toggle("show");
// Update button text based on sidebar visibility
if (sidebar.classList.contains("show")) {
$('#toggle-btn').html('<?php echo $locale_text["GPSMAP_menu_hide"]; ?>');
} else {
$('#toggle-btn').html('<?php echo $locale_text["GPSMAP_menu_show"]; ?>');
}
}
//=============================================================================
// get directions from degrees
//=============================================================================
function getDirection(degrees) {
const directions = ['N', 'NE', 'E', 'SE', 'S', 'SW', 'W', 'NW'];
const anglePerDirection = 45;
const offsetAngle = 22.5;
// Normalize degrees to be within 0-360
const adjustedDegrees = (degrees % 360 + 360) % 360;
// Determine which of the 8 segments it falls into
const index = Math.floor((adjustedDegrees + offsetAngle) / anglePerDirection) % directions.length;
const direction = directions[index];
return direction;
}
//=============================================================================
// Search for customers
//=============================================================================
function customerSearch() {
var createJobWindow = $("#createJobWindow");
if(createJobWindow.is(":visible")){
createJobWindow.addClass("hidden");
resetCreateJobWindow();
}
mapGlobals.custSearching = true;
var searchString = $("#customerSearchVal").val();
mapGlobals.searchString = searchString;
if(searchString == "<?php echo $locale_text["GPSMAP_writesearch"];?>"){
$("#customerSearchVal").val("");
searchString = "";
}
// Prevent duplicate requests
if (searchString === lastSearchString) {
return;
}
lastSearchString = searchString;
getCustomerList(searchString);
}
//=============================================================================
// Get a customer list. If only one customer, info is filled otherwise a list is show with possible customers
//=============================================================================
function getCustomerListTimer(bypassTimer) {
clearTimeout(inputTimer);
var searchString = $("#customerSearchVal").val();
if (searchString.length < 3) {
return;
} else if (searchString.length === 3) {
customerSearch();
return;
}
if (!bypassTimer) {
inputTimer = setTimeout(function () {getCustomerListTimer(true);}, 500);
return;
}
customerSearch();
}
//=============================================================================
// Get a customer list
//=============================================================================
function getCustomerList(searchString){
// Limit queue to one pending request
if (custQueueCount > 0) {
$.ajaxq.clear("custSearchQueue");
custQueueCount = Math.max(custQueueCount - 1, 0); // Decrement the queue count safely
}
custQueueCount++;
parmData = 'searchString='+encodeURI(searchString)
;
$.ajaxq("custSearchQueue", {
cache: false,
url: "ajax_get_customer_search.php",
dataType: "json",
data: parmData,
success: function(jsonData){
//console.log("Search request completed for:", searchString);
//console.log(jsonData);
if (jsonData["error"] != "") {
console.log(jsonData["error"]);
} else {
delete jsonData["error"];
mapGlobals.JSON_customerList = jsonData;
if (custPopoverVisible) {
$(".popover-body").empty(); // Clear previous popover content
} else {
// Ensure no old data lingers in the popover if it's not visible
$("#customerSearchBtn").popover("dispose"); // Completely remove the popover instance
}
var resultHTML = "<div id='customerSearchResult' class='resultList'></div>";
var liCount = 0;
resultHTML += "<ul id='customerSearchResultUL' class='searchResultList list-group'>";
$.each( jsonData, function( index, item ){
if (item.length !== 0) {
liCount++;
const ID = item.id;
const referenceID = item.code;
mapGlobals.JSON_customerListID[referenceID] = ID;
const name = item.name;
const location = ((!item.location || item.location.toUpperCase() == 'NULL') ? "" : item.location);
const street = ((!item.street || item.street.toUpperCase() == 'NULL') ? "" : item.street);
const zipCode = ((!item.zipCode || item.zipCode.toUpperCase() == 'NULL') ? "" : item.zipCode);
const city = ((!item.city || item.city.toUpperCase() == 'NULL') ? "" : item.city);
const country = ((!item.country || item.country.toUpperCase() == 'NULL') ? "" : item.country);
var display = "("+referenceID+") "+name;
const displayHit = display.toUpperCase().includes(searchString.toUpperCase());
const locationHit = location.toUpperCase().includes(searchString.toUpperCase());
const streetHit = street.toUpperCase().includes(searchString.toUpperCase());
const zipCodeHit = zipCode.toUpperCase().includes(searchString.toUpperCase());
const cityHit = city.toUpperCase().includes(searchString.toUpperCase());
const countryHit = country.toUpperCase().includes(searchString.toUpperCase());
resultHTML += "<li id='refID_" + referenceID + "' class='custResultLink cursorPointer list-group-item'>" + (displayHit ? "<b>" : "") + display + (displayHit ? "</b>" : "") + ", " + (locationHit ? "<b>" : "") + (location != "" ? location + ", " : "") + (locationHit ? "</b>" : "") + (streetHit ? "<b>" : "") + (street != "" ? street + ", " : "") + (streetHit ? "</b>" : "") + (zipCodeHit ? "<b>" : "") + zipCode + (zipCodeHit ? "</b>" : "") + " "+ (cityHit ? "<b>" : "") + city + (cityHit ? "</b>" : "") + " "+ (countryHit ? "<b>" : "") + country + (countryHit ? "</b>" : "") + "</li>";
}
});
resultHTML += "</ul>";
if (liCount == 0){ // No result
resultHTML += "<?php echo $locale_text["GPSMAP_noData"]; ?>";
mapGlobals.custSearching = false;
}
resultHTML += "</div>";
// Update content directly in the popover if it is already visible
if (custPopoverVisible) {
$(".popover-body").empty().html(resultHTML);
// Re-bind click events to the updated content
$('.popover-body .custResultLink').off('click').on('click', function () {
var thisId = this.id;
var idArr = thisId.split("_");
if (mapGlobals.currentCustomerMarker !== undefined) {
removeMarker(mapGlobals.currentCustomerMarker);
}
for (var i in mapGlobals.selectedMachines) {
removeMarker(mapGlobals.selectedMachines[i]);
if (i in mapGlobals.selectedMachines) { // If key exists, it might not because of async reverse geo code
delete mapGlobals.selectedMachines[i];
}
}
fillInCustomerInfo(idArr[1]);
$("#hideCustomerBtn").removeClass('d-none');
$('#createCustomerJobBtn').prop('disabled', false);
$("#refFilterResult").hide();
$("#refFilter").val("");
});
} else {
// Initialize the popover if not visible
$("#customerSearchBtn").popover({
title: "<?php echo $locale_text['GPSMAP_Machine']; ?>",
trigger: "manual",
placement: "bottom",
html: true,
customClass: "wide-popover",
content: resultHTML
});
$("#customerSearchBtn").popover("show");
custPopoverVisible = true;
// Bind click events to the newly created content
$('.popover-body .custResultLink').off('click').on('click', function () {
var thisId = this.id;
var idArr = thisId.split("_");
if (mapGlobals.currentCustomerMarker !== undefined) {
removeMarker(mapGlobals.currentCustomerMarker);
}
for (var i in mapGlobals.selectedMachines) {
removeMarker(mapGlobals.selectedMachines[i]);
if (i in mapGlobals.selectedMachines) { // If key exists, it might not because of async reverse geo code
delete mapGlobals.selectedMachines[i];
}
}
fillInCustomerInfo(idArr[1]);
$("#hideCustomerBtn").removeClass('d-none');
$('#createCustomerJobBtn').prop('disabled', false);
$("#refFilterResult").hide();
$("#refFilter").val("");
});
}
}
},
complete: function () {
custQueueCount = Math.max(custQueueCount - 1, 0); // Decrement the queue count safely
},
error: function (xhr, status, error) {
if (status !== "abort") {
console.error("AJAX error:", status, error);
}
}
});
}
//=============================================================================
// Fill customer infobox for specific customer
//=============================================================================
function fillInCustomerInfo(refID){
$.each( mapGlobals.JSON_customerList, function( index, item ){
if(item.referenceID == refID){
const location = ((!item.location || item.location.toUpperCase() == 'NULL') ? "" : item.location);
const street = ((!item.street || item.street.toUpperCase() == 'NULL') ? "" : item.street);
const zipCode = ((!item.zipCode || item.zipCode.toUpperCase() == 'NULL') ? "" : item.zipCode);
const city = ((!item.city || item.city.toUpperCase() == 'NULL') ? "" : item.city);
const country = ((!item.country || item.country.toUpperCase() == 'NULL') ? "" : item.country);
$("#customerSearchResult").hide();
$("#customerSearchResultUL").html("");
$("#customerSearchVal").val("");
$("#jobCreateModal-custId").val(item.id);
$("#jobCreateModal-serviceunitId").val("");
mapGlobals.selectedCustomer = item;
$("#createJobFromCustomerBtn").removeClass("mapBtnDisabled");
$("#custNumber").html("<span>"+item.code+"</span>" + "<span id='customerInfoIcon' class='listIconInfo'><i class='fa-solid fa-circle-info'></i></span>");
$("#custName").html(item.name);
$("#custLocation").html(location);
$("#custAddress").html(street);
$("#custZipCity").html(zipCode + " " + city);
if(item.streetName != ""){
var address = street + " " + zipCode + " " + city + " " + country;
var markerData = new MarkerData("customer", null, address, "ref_" + item.referenceID, item.name);
geoCodeAddress(markerData);
}
return false; //breake out
}
});
// Show the popover
$("#customerSearchBtn").popover("hide");
custPopoverVisible = false; // Set the popover state to hidden
$.ajaxq.abort("custSearchQueue"); //aborts the current search request
// Attach hover handler to .listIconInfo within the search results
$("#customerContentWrapper").on("mouseenter", ".listIconInfo", function () {
const $icon = $(this);
// Dispose of any existing popover to prevent duplicates and clear any previous timeout
$icon.popover("dispose");
//notice no extra data (øvrige data) in 2.0
var resultHTML = '<div class="row">';
resultHTML += '<div class="col-3 fw-bold"><?php echo $locale_text["GPSMAP_number"]; ?></div>';
resultHTML += '<div class="col-3 border-end">' + mapGlobals.selectedCustomer.referenceID + '</div>';
resultHTML += '<div class="col-3 fw-bold"><?php echo $locale_text["GPSMAP_contact_person"]; ?></div>';
resultHTML += '<div class="col-3">' + (mapGlobals.selectedCustomer.contactName !== null && mapGlobals.selectedCustomer.contactName.toUpperCase() != 'NULL' ? mapGlobals.selectedCustomer.contactName : "") + '</div>';
resultHTML += "</div>";
resultHTML += '<div class="row">';
resultHTML += '<div class="col-3 fw-bold"><?php echo $locale_text["GPSMAP_name"]; ?></div>';
resultHTML += '<div class="col-3 border-end">' + mapGlobals.selectedCustomer.name + '</div>';
resultHTML += '<div class="col-3 fw-bold"><?php echo $locale_text["GPSMAP_phone"]; ?></div>';
resultHTML += '<div class="col-3">' + (mapGlobals.selectedCustomer.contactPhone !== null && mapGlobals.selectedCustomer.contactPhone.toUpperCase() != 'NULL' ? mapGlobals.selectedCustomer.contactPhone : "") + '</div>';
resultHTML += "</div>";
resultHTML += '<div class="row">';
resultHTML += '<div class="col-3 fw-bold"><?php echo $locale_text["GPSMAP_location"]; ?></div>';
resultHTML += '<div class="col-3 border-end">' + (mapGlobals.selectedCustomer.location !== null && mapGlobals.selectedCustomer.location.toUpperCase() != 'NULL' ? mapGlobals.selectedCustomer.location : "") + '</div>';
resultHTML += '<div class="col-3 fw-bold"><?php echo $locale_text["GPSMAP_cell_phone"]; ?></div>';
resultHTML += '<div class="col-3">' + (mapGlobals.selectedCustomer.contactMobile !== null && mapGlobals.selectedCustomer.contactMobile.toUpperCase() != 'NULL' ? mapGlobals.selectedCustomer.contactMobile : "") + '</div>';
resultHTML += "</div>";
resultHTML += '<div class="row">';
resultHTML += '<div class="col-3 fw-bold"><?php echo $locale_text["GPSMAP_address"]; ?></div>';
resultHTML += '<div class="col-3 border-end">' + (mapGlobals.selectedCustomer.street !== null && mapGlobals.selectedCustomer.street.toUpperCase() != 'NULL' ? mapGlobals.selectedCustomer.street : "") + '</div>';
resultHTML += '<div class="col-3 fw-bold"><?php echo $locale_text["GPSMAP_fax"]; ?></div>';
resultHTML += '<div class="col-3">' + (mapGlobals.selectedCustomer.contactFax !== null && mapGlobals.selectedCustomer.contactFax.toUpperCase() != 'NULL' ? mapGlobals.selectedCustomer.contactFax : "") + '</div>';
resultHTML += "</div>";
resultHTML += '<div class="row">';
resultHTML += '<div class="col-3 fw-bold"><?php echo $locale_text["GPSMAP_zip_city"]; ?></div>';
resultHTML += '<div class="col-3 border-end">' + (mapGlobals.selectedCustomer.zipCode !== null && mapGlobals.selectedCustomer.zipCode.toUpperCase() != 'NULL' ? mapGlobals.selectedCustomer.zipCode : "") + " " + (mapGlobals.selectedCustomer.city !== null && mapGlobals.selectedCustomer.city.toUpperCase() != 'NULL' ? mapGlobals.selectedCustomer.city : "") + '</div>';
resultHTML += '<div class="col-3 fw-bold"><?php echo $locale_text["GPSMAP_email"]; ?></div>';
resultHTML += '<div class="col-3">' + (mapGlobals.selectedCustomer.contactEmail !== null && mapGlobals.selectedCustomer.contactEmail.toUpperCase() != 'NULL' ? mapGlobals.selectedCustomer.contactEmail : "") + '</div>';
resultHTML += "</div>";
resultHTML += '<div class="row">';
resultHTML += '<div class="col-3 fw-bold"><?php echo $locale_text["GPSMAP_country"]; ?></div>';
resultHTML += '<div class="col-3 border-end">' + (mapGlobals.selectedCustomer.country !== null && mapGlobals.selectedCustomer.country.toUpperCase() != 'NULL' ? mapGlobals.selectedCustomer.country : "") + '</div>';
resultHTML += '<div class="col-3 fw-bold"></div>';
resultHTML += '<div class="col-3"></div>';
resultHTML += "</div>";
// Initialize popover with the fetched content
$icon.popover({
title: "<?php echo $locale_text["GPSMAP_info"] ?>",
trigger: "manual",
placement: "right",
html: true,
customClass: "wide-popover",
content: resultHTML
});
// Attach the event listener for 'inserted.bs.popover' before showing the popover
$icon.on('inserted.bs.popover', function () {
// Bind click event to each .custResultLine element within the popover
$('.popover-body .custResultLine').on('click', function () {
fillInMachineInfo(this.id);
});
});
// Show the popover
$icon.popover("show");
});
// TODO handle machines from TEO
var customerReferenceListContainer = $("#customerReferenceListContainer");
customerReferenceListContainer.addClass("d-none");
$("#referenceSearchWrapper").addClass("d-none");
getServiceUnitList(refID);
/*
var customerReferenceListContainer = $("#customerReferenceListContainer");
customerReferenceListContainer.addClass("hidden");
var refFilter = $("#referenceSearchWrapper");
refFilter.addClass("hidden");
getReferenceList(refID, "ul", customerReferenceListContainer);
*/
}
//=============================================================================
// Fill infoWindow
//=============================================================================
function fillInfoWindow(markerData){
var html = "";
switch(markerData.type){
case "customer" :
html = "<div>" +
"<div class='infoWindowHeader'>" +
markerData.description +
"</div>" +
"<div class='infoWindowInfoCol'>" +
$("#custLocation").text() + "<br />" +
$("#custAddress").text() + "<br />" +
$("#custZipCity").text() +
"</div>" +
"</div>";
break;
case "home" :
html = "<div>" +
"<div class='infoWindowHeader'>" +
"<?php echo $locale_text["GPSMAP_legendHome"]; ?>" +
"</div>" +
"<div style='margin-top: 5px;'>" +
markerData.address +
"</div>" +
"</div>" +
"</div>";
break;
case "department" :
var IDarray = markerData.id.split('_');
var theID = "dep_"+ IDarray[1];
var contactInfo = "";
if (mapGlobals.JSON_departmentContact[theID]["contactPerson"]) contactInfo += mapGlobals.JSON_departmentContact[theID]["contactPerson"];
if (mapGlobals.JSON_departmentContact[theID]["contactEmail"]) contactInfo += ", " + mapGlobals.JSON_departmentContact[theID]["contactEmail"];
if (mapGlobals.JSON_departmentContact[theID]["contactPhoneNumber"]) contactInfo += ", " + mapGlobals.JSON_departmentContact[theID]["contactPhoneNumber"];
if (mapGlobals.JSON_departmentContact[theID]["contactMobilePhoneNumber"]) contactInfo += ", " + mapGlobals.JSON_departmentContact[theID]["contactMobilePhoneNumber"];
if (mapGlobals.JSON_departmentContact[theID]["contactFaxNumber"]) contactInfo += ", " + mapGlobals.JSON_departmentContact[theID]["contactFaxNumber"];
html = "<div>" +
"<div class='infoWindowHeader'>" +
markerData.description +
"</div>" +
"<div style='margin-top: 5px;'>" +
markerData.address +
"</div>" +
"<div style='margin-top: 5px;'>" +
contactInfo +
"</div>" +
"</div>";
break;
case "machine" :
html = "<div>" +
"<div class='infoWindowHeader'>" +
markerData.description +
"</div>" +
"<div style='margin-top: 5px;'>" +
markerData.address +
"</div>" +
"</div>" +
"</div>";
break;
}
return html;
}
//=============================================================================
// jQuery - Ready
//=============================================================================
$(document).ready(function() {
$("#loginOrganizationName").html(" " + loginOrganizationName);
//get initial data from REEFT 2.0
getJobstatus();
getActTypes();
// rotate carets
document.querySelectorAll('.btn-toggle').forEach(button => {
button.addEventListener('click', function () {
const icon = this.querySelector('.toggle-icon');
icon.classList.toggle('rotate');
});
});
// show sidebar at load
toggleSidebar();
$("#job-message").addClass("d-none");
$('.sideCollapsible').on('shown.bs.collapse', function () {
if ($('#empInfoModal').hasClass('show')) {
$('#empInfoModal').modal('hide'); // Close the modal
}
})
// Customer search result hover effect
$("#customerSearchResult, #addressSearchResult").on("mouseenter", "li.custResultLink", function(){
$(this).addClass("custResultLinkHover");
});
// Customer search result hover effect
$("#customerSearchResult, #addressSearchResult").on("mouseleave", "li.custResultLink", function(){
$(this).removeClass("custResultLinkHover");
});
// Reset the value on page refresh
$('#addressSearchVal').val('');
$('#customerSearchVal').val('');
// Perform customer search
$("#customerSearchBtn").click(function(){
var searchString = $("#customerSearchVal").val();
if (searchString.length < 3) {
alert("<?php echo $locale_text["ERROR_MIN3"]; ?>");
} else {
customerSearch();
}
});
// Remove customer
$("#hideCustomerBtn").click(function(event){
if(mapGlobals.currentCustomerMarker != undefined){
removeMarker(mapGlobals.currentCustomerMarker);
}
for(var i in mapGlobals.selectedMachines){
removeMarker(mapGlobals.selectedMachines[i]);
if(i in mapGlobals.selectedMachines){ // if key exists, it might not because of async revers geo code
delete mapGlobals.selectedMachines[i];
}
}
mapGlobals.selectedCustomer = "";
$("#custNumber").html("");
$("#custName").html("");
$("#custLocation").html("");
$("#custAddress").html("");
$("#custZipCity").html("");
$("#refFilter").val("");
$("#createJobFromCustomerBtn").addClass("mapBtnDisabled");
if($("#createJobWindow").is(":visible")){
$("#createJobWindow").addClass("hidden");
resetCreateJobWindow();
}
$("#customerReferenceListContainer").addClass("d-none");
$("#refFilterResult").hide();
$("#referenceSearchWrapper").hide();
mapGlobals.custSearching = false;
$("#hideCustomerBtn").addClass('d-none');
$('#createCustomerJobBtn').prop('disabled', true);
});
//create job for selected customer
$("#createCustomerJobBtn").click(function(){
if(mapGlobals.selectedCustomer != ""){
jobCreate(mapGlobals.selectedCustomer);
}
else{
alert("<?php echo $locale_text["ERROR_CHOOSE_CUST"]; ?>");
}
});
// Select an employee and shows it on map
$("#employeeListContainer").on("click", ".listItem", function(){
//if(!mapGlobals.depListShow){ // If deplist is shown click wont fire on emplist.
var personID = $(this).attr("id").split("_");
personID = personID[1];
if(!$(this).hasClass("listItemOn")){
if (typeof carData[personID] !== "undefined" && carData[personID] !== null) {
var markerData = carData[personID]["marker"];
addMarker(markerData);
if(mapGlobals.callMapCenter){ // Is not called when using show all btn to trigger click on the listitem.
setMapCenter(markerData.latLng);
}
if($.inArray(personID, mapGlobals.selectedEmployees) == -1){
mapGlobals.selectedEmployees.push(personID);
}
$(this).addClass("listItemOn");
} else {
alert("<?php echo $locale_text["GPSMAP_unknown_location"]; ?>");
}
}
else{
removeEmployeeMarker(personID);
var persIndex = $.inArray(personID, mapGlobals.selectedEmployees);
if(persIndex != -1){
mapGlobals.selectedEmployees.splice(persIndex, 1);
}
$(this).removeClass("listItemOn");
}
//}
});
// Show all gps emps
$("#showAllEmpsBtn").click(function(){
mapGlobals.callMapCenter = false;
$(this).addClass("d-none");
$("#hideAllEmpsBtn").removeClass("d-none");
$("#employeeListContainer li.listItem:not(.listItemOn):not(.d-none)").each(function(index){
$(this).trigger("click");
});
mapGlobals.callMapCenter = true;
});
// Hide all gps emps
$("#hideAllEmpsBtn").click(function(){
$(this).addClass("d-none");
$("#showAllEmpsBtn").removeClass("d-none");
var markers = mapGlobals.markerCluster.getMarkers();
var markersEmpArr = [];
for (i = 0; i < markers.length; i++) {
var markerId = markers[i].id.split("_");
if(markerId[0] == "em") {
markersEmpArr.push(markers[i]);
}
}
mapGlobals.markerCluster.removeMarkers(markersEmpArr);
$("#employeeListContainer li.listItemOn:not(.d-none)").each(function(){
$(this).trigger("click");
});
});
// Select a job and shows it on map
$("#jobListContainer").on("click", ".jobListItem", function(){
const targetClass = event.target.className;
var jobID = $(this).attr("id").split("_");
jobID = jobID[1];
const thisJob = mapGlobals.JSON_jobList[jobID];
const thisJobAdress = mapGlobals.JSON_jobList[jobID]["address"];
var address = thisJob.address;
var description = thisJob.shortDescription;
var taskStatus = thisJob.jobStatus;
var taskLatitude = thisJob.latitude;
var taskLongitude = thisJob.longitude;
var taskPlannedperson = thisJob.assignedTo;
var taskUuid = thisJob.id;
var customerUuid = thisJob.customerId;
var serviceUnitUuid = thisJob.serviceUnitId;
var markerID = $(this).attr("id");
var thisLi = $(this);
if(address != ""){
$(this).toggleClass("listItemOn");
if($(this).hasClass("listItemOn")){
var markerData = new MarkerData("job", "", address, markerID, description, "", taskLatitude, taskLongitude,"",taskStatus, taskPlannedperson, taskUuid);
geoCodeAddress(markerData);
}
else{
removeMarker(mapGlobals.selectedJobs[markerID]);
mapGlobals.markerCluster.removeMarker(mapGlobals.selectedJobs[markerID]);
if(markerID in mapGlobals.selectedJobs){ // if key exists, it might not because of async revers geo code
delete mapGlobals.selectedJobs[markerID];
}
}
} else {
parmData = 'taskUuid='+taskUuid
;
if (typeof customerUuid !== "undefined" && customerUuid !== null && customerUuid != "null") parmData += '&customerUuid='+customerUuid;
if (typeof serviceUnitUuid !== "undefined" && serviceUnitUuid !== null && serviceUnitUuid != "null") parmData += '&serviceUnitUuid='+serviceUnitUuid;
$.ajax({
cache: false,
url: "ajax_get_jobaddress.php",
data: parmData,
dataType: "json",
success: function(jsonDataAddr){
//console.log(jsonDataAddr);
if (jsonDataAddr["error"] != "") {
console.log(jsonDataAddr["error"]);
} else {
delete jsonDataAddr["error"];
const jobData = jsonDataAddr[0];
const longitude = jobData["longitude"];
const latitude = jobData["latitude"];
const location = jobData["location"];
const street = jobData["street"];
const zipCode = jobData["zipCode"];
const city = jobData["city"];
const country = jobData["country"];
var address = location + " " + street + " " + zipCode + " " + city + " " + country;
address = address.replace("null", "").trim();
if(address != ""){
thisLi.toggleClass("listItemOn");
if (taskLatitude != "") taskLatitude = latitude;
if (taskLongitude != "") taskLongitude = longitude;
if(thisLi.hasClass("listItemOn")){
var markerData = new MarkerData("job", "", address, markerID, description, "", taskLatitude, taskLongitude,"",taskStatus, taskPlannedperson, taskUuid);
geoCodeAddress(markerData);
}
else{
removeMarker(mapGlobals.selectedJobs[markerID]);
mapGlobals.markerCluster.removeMarker(mapGlobals.selectedJobs[markerID]);
if(markerID in mapGlobals.selectedJobs){ // if key exists, it might not because of async revers geo code
delete mapGlobals.selectedJobs[markerID];
}
}
}
}
},
error: function(xhr, status, error) {
console.log(error);
}
});
}
});
// Show all jobs
$("#showAllJobsBtn").click(function(){
mapGlobals.callMapCenter = false;
$(this).addClass("d-none");
$("#hideAllJobsBtn").removeClass("d-none");
$("#jobListContainer li.listItem:not(.listItemOn):not(.d-none)").each(function(index){
$(this).trigger("click");
});
mapGlobals.callMapCenter = true;
});
// Hide all jobs
$("#hideAllJobsBtn").click(function(){
$(this).addClass("d-none");
$("#showAllJobsBtn").removeClass("d-none");
var markers = mapGlobals.markerCluster.getMarkers();
var markersEmpArr = [];
for (i = 0; i < markers.length; i++) {
var markerId = markers[i].id.split("_");
if(markerId[0] == "job") {
markersEmpArr.push(markers[i]);
}
}
mapGlobals.markerCluster.removeMarkers(markersEmpArr);
$("#jobListContainer li.listItemOn:not(.d-none)").each(function(){
$(this).trigger("click");
});
});
// select all for printing.
$("#printGPSTransactionsAllCheck").click(function(){
var elementsArray = document.getElementsByClassName("historyMenuPrint");
for(var i=0; i<elementsArray.length; i++) {
var checked = elementsArray[i].checked;
if (document.getElementById("printGPSTransactionsAllCheck").checked == true) {
elementsArray[i].checked = true;
} else {
elementsArray[i].checked = false;
}
}
recalculateSummations();
});
// Hide popover when clicking outside
$(document).on("click", function (e) {
const $target = $(e.target);
// Close #customerSearchBtn popover
if (!$target.closest("#customerSearchBtn").length && !$target.closest(".popover").length) {
$("#customerSearchBtn").popover("hide");
custPopoverVisible = false;
}
// Close #customerInfoIcon popovers
if (!$target.closest("#customerInfoIcon").length && !$target.closest(".popover").length) {
$("#customerInfoIcon").popover("hide");
}
// Close addressSearchBtn popovers
if (!$target.closest("#addressSearchBtn").length && !$target.closest(".popover").length) {
$("#addressSearchBtn").popover("hide");
}
// Close addressSearchBtn popovers
if (!$target.closest(".servUnitInfoIcon").length && !$target.closest(".popover").length) {
$(".servUnitInfoIcon").popover("hide");
}
// Close addressSearchBtn popovers
if (!$target.closest(".jobSearchIcon").length && !$target.closest(".popover").length) {
$(".jobSearchIcon").popover("hide");
// Cancel any pending popover initialization due to hoverTimeout
clearTimeout(hoverTimeout);
}
});
// Open pop up window and print dialog.
$("#printGPSTransactions").click(function(){
var printRoutes = false;
var printJobs = false;
var printSchemes = false;
var printSomething = false;
var elementsArray = document.getElementsByClassName("historyMenuPrint");
for(var i=0; i<elementsArray.length; i++) {
var id = elementsArray[i].id;
var idArr = id.split("_");
var checked = elementsArray[i].checked;
if (checked) {
printSomething = true;
if (idArr[0] == "printRoute") printRoutes = true;
else if (idArr[0] == "printJob") printJobs = true;
else if (idArr[0] == "printScheme") printSchemes = true;
}
}
if (printSomething) {
$("#modal-message").removeClass("d-none").html( '<?php echo $locale_text["GPSMAP_print_wait"] ?>' );
var printWin = window.open("", "printTransactions", "scrollbars=1,height=800,width=800");
// Clear the content of the window
printWin.document.open();
printWin.document.writeln(""); // Optional, just to ensure it's visibly cleared
printWin.document.close();
var style = "<style>" +
"body{font-family: sans-serif;} " +
"table{border-collapse:collapse;} " +
"table.dataTable { width: 100%;} " +
"th{border-bottom: 2px solid #CCCCCC; font-size: small; padding: 5px 5px; text-align: left;} " +
"th{font-weight: bold;} " +
"td{padding: 5px; border-bottom: 1px solid #E6E6E6; text-align: left;} " +
".empHeader{ margin-top: 5px; margin-bottom: 5px; font-weight: bold; }" +
".jobListIconMenu{ float: right;}" +
"#empJobList{ list-style: none; padding: 0px;}" +
".jobInfoLine{ overflow: hidden; text-overflow: ellipsis; white-space: nowrap;} " +
".jobInfoLineHeader{ overflow: hidden; text-overflow: ellipsis; white-space: nowrap; font-weight: bold;}" +
".historyMenuPrint{display:none}" +
"#empDayJobContainer{border-bottom: 1px solid #E6E6E6;}" +
".jobListItem{border-top: 1px solid #E6E6E6;}" +
".jobListAssignmentItem{border-top: 1px dotted #E6E6E6;}" +
"</style>";
printWin.document.writeln(style);
}
if (printRoutes) {
printWin.document.writeln('<div id="empDayJobContainerHeader" class="empHeader"><?php echo $locale_text["GPSMAP_route_stoptime"]; ?></div>');
printWin.document.writeln('<table class="dataTable roundAllOver" cellspacing="0">');
printWin.document.writeln('<tbody id="summationDataSection" class="dataTable">');
var routeArray = document.getElementsByClassName("printRoute");
// Add the header to the table
printWin.document.writeln("<tr>");
var headerArr = excelData[0];
var rowContent = "";
for (var key in headerArr) {
if (headerArr.hasOwnProperty(key) && key !='durationRaw') {
var cell = headerArr[key];
rowContent += "<th>"+cell+"</th>";
}
}
printWin.document.writeln(rowContent); // Create a table cell for each column
printWin.document.writeln("</tr>");
for(var i=0; i<routeArray.length; i++) {
var checked = routeArray[i].checked;
if (checked) {
printWin.document.writeln("<tr>");
var rowArr = excelData[i+1];
var rowContent = "";
for (var key in rowArr) {
if (rowArr.hasOwnProperty(key) && key !='durationRaw') {
var cell = rowArr[key];
rowContent += "<td>"+cell+"</td>";
}
}
printWin.document.writeln(rowContent); // Create a table cell for each column
printWin.document.writeln("</tr>");
}
}
// Add the footer to the table
printWin.document.writeln("<tr>");
var footerArr = excelData[excelData.length - 1];
var rowContent = "";
for (var key in footerArr) {
if (footerArr.hasOwnProperty(key) && key !='durationRaw') {
var cell = footerArr[key];
rowContent += "<th>"+cell+"</th>";
}
}
printWin.document.writeln(rowContent); // Create a table cell for each column
printWin.document.writeln("</tr>");
printWin.document.writeln('</tbody>');
printWin.document.writeln('</table>');
}
if (printJobs) {
printWin.document.writeln('<div id="empDayJobContainerHead" class="empHeader"><?php echo $locale_text["GPSMAP_job"]; ?></div>');
printWin.document.writeln('<div id="empDayJobContainer">');
printWin.document.writeln('<ul id="empJobList">');
var jobArray = document.getElementsByClassName("printJob");
for(var i=0; i<jobArray.length; i++) {
var checked = jobArray[i].checked;
if (checked) {
var parentLI = jobArray[i].closest("li");
printWin.document.writeln(parentLI.outerHTML)
}
}
printWin.document.writeln('</ul>');
printWin.document.writeln('</div>');
}
if (printSchemes) {
// Add the header to the table
printWin.document.writeln('<div id="empTimeshemeContainerHead" class="empHeader"><?php echo $locale_text["GPSMAP_timesheet"]; ?></div>');
printWin.document.writeln('<div id="empTimeshemeContainer">');
printWin.document.writeln('<table class="dataTable roundAllOver" cellspacing="0">');
printWin.document.writeln('<tbody id="sumationDataSection" class="dataTable">');
printWin.document.writeln('<table id="empSchemeList" class="dataTable roundAllOver"><thead class="dataTable"><tr>');
printWin.document.writeln('<th><?php echo $locale_text["GPSMAP_job"]; ?></th>');
printWin.document.writeln('<th><?php echo $locale_text["GPSMAP_customer"]; ?></th>');
printWin.document.writeln('<th><?php echo $locale_text["GPSMAP_address"]; ?></th>');
printWin.document.writeln('<th><?php echo $locale_text["GPSMAP_salary_type"]; ?></th>');
printWin.document.writeln('<th><?php echo $locale_text["GPSMAP_duration"]; ?></th>');
printWin.document.writeln('<th></th>');
printWin.document.writeln('</tr></thead>');
printWin.document.writeln('<tbody>');
var schemeArray = document.getElementsByClassName("printScheme");
for (var i = 0; i < schemeArray.length; i++) {
var checked = schemeArray[i].checked;
if (checked) {
printWin.document.writeln("<tr>");
// Find the closest row (div with class "row")
var parentRow = schemeArray[i].closest(".row");
if (parentRow) {
// Select all divs where the class starts with "col-"
var cols = parentRow.querySelectorAll('[class^="col-"]');
// Extract text content from each column
var colTexts = [];
for (var j = 0; j < cols.length; j++) {
var text = cols[j].textContent.trim();
printWin.document.writeln("<td>"+text+"</td>");
}
}
printWin.document.writeln("</tr>");
}
}
printWin.document.writeln('</tbody>');
printWin.document.writeln('</table>');
printWin.document.writeln('</div>');
}
if (printSomething) {
$("#modal-message").addClass("d-none").html( '' );
printWin.print();
printWin.document.close(); // IE wont print unless document is closed.
}
});
// Open pop up window and print dialog.
$("#printPeriodGPSTransactions").click(function(){
$("#modal-message").removeClass("d-none").html( '<?php echo $locale_text["GPSMAP_print_wait"] ?>' );
var printWin = window.open("", "printTransactions", "scrollbars=1,height=800,width=800");
var style = "<style>" +
"body{font-family: sans-serif;} " +
"table{border-collapse:collapse;} " +
"th{border-bottom: 2px solid #CCCCCC; border-top: 2px solid #CCCCCC; font-size: small; padding: 5px 5px; text-align: left;} " +
"th{font-weight: bold;} " +
"td{padding: 5px; border-bottom: 1px solid #E6E6E6; text-align: left;} " +
".empHeader{ margin-top: 5px; margin-bottom: 5px; font-weight: bold; }" +
"</style>";
printWin.document.writeln(style);
printWin.document.writeln($("#selectedEmp").html());
printWin.document.writeln('<div id="periodContainerHeader" class="empHeader"><?php echo $locale_text["GPSMAP_route_stoptime"]; ?></div>');
printWin.document.writeln('<table class="dataTable roundAllOver" cellspacing="0">');
printWin.document.writeln('<tbody id="summationDataSection" class="dataTable">');
for(var i=0; i<excelData.length; i++) {
printWin.document.writeln("<tr>");
var rowArr = excelData[i];
var rowContent = "";
for (var key in rowArr) {
if (rowArr.hasOwnProperty(key) && key !='durationRaw') {
var cell = rowArr[key];
rowContent += "<td>"+cell+"</td>";
}
}
printWin.document.writeln(rowContent); // Create a table cell for each column
printWin.document.writeln("</tr>");
}
printWin.document.writeln('</tbody>');
printWin.document.writeln('</table>');
$("#modal-message").addClass("d-none").html( '' );
printWin.print();
printWin.document.close(); // IE wont print unless document is closed.
});
//Excel buttons (day and period)
$(".excelBtn").click(function(){
var filename = "Reeft_GPS";
var ws = XLSX.utils.json_to_sheet(excelData, {skipHeader:true});
var wb = XLSX.utils.book_new();
// calculate column width
const jsonKeys = Object.keys(excelData[0]);
var objectMaxLength = [];
for (var i = 0; i < excelData.length; i++) {
var value = excelData[i];
for (var j = 0; j < jsonKeys.length; j++) {
if (typeof value[jsonKeys[j]] == "number") {
objectMaxLength[j] = 10;
} else {
const l = (value[jsonKeys[j]] ? value[jsonKeys[j]].length : 0);
objectMaxLength[j] = (objectMaxLength[j] >= l ? objectMaxLength[j] : l);
}
}
}
const wscols = objectMaxLength.map(w => { return { wch: w} }); //width, wch (char), wpx (pixel)
ws["!cols"] = wscols;
XLSX.utils.book_append_sheet(wb, ws, filename);
XLSX.writeFile(wb,filename+".xlsx");
});
// customer search
$(document).keydown(function(event){
if(!mapGlobals.custSearching){
if($("#customerSearchVal").is(":focus")){
if(event.keyCode == 13){ // Enter
$("#custSearhBtn").trigger("click");
}
}
}
if(event.keyCode == 27){ // Escape
if($("#customerSearchResult").is(":visible")){
mapGlobals.custSearching = false;
$("#customerSearchResult")
.hide()
.html("");
$("#customerSearchResultUL").html("");
}
}
});
// Selected departments
$("#depListDropDown").change(function() {
var selectedDep = $('#depListDropDown').val();
const employeeShowOffline = $('#employeeShowOffline').prop('checked');
if (selectedDep.length > 0) {
// Iterate through each <li> in the list
$("#employeeList li").each(function() {
// Get the class list of the current <li>
const classList = $(this).attr("class");
var id = $(this).attr("id");
var idArr = id.split("_");
var imei = idArr[1];
var markerData = carData[imei]["marker"];
// Find the part after "dep_"
// [\w-] matches word characters (letters, digits, underscores) and hyphens (-).
const depMatch = classList.match(/dep_([\w-]+)/);
if (depMatch && selectedDep.includes(depMatch[1])) {
if (employeeShowOffline || !$(this).hasClass('isOffline') ) {
$(this).removeClass('d-none');
addMarker(markerData);
$("#emp_" + imei).addClass("listItemOn");
}
} else {
$(this).addClass('d-none');
removeEmployeeMarker(imei);
$("#emp_" + imei).removeClass("listItemOn");
}
});
} else {
$("#employeeList li").each(function() {
// Get the class list of the current <li>
const classList = $(this).attr("class");
var id = $(this).attr("id");
var idArr = id.split("_");
var imei = idArr[1];
var markerData = carData[imei]["marker"];
if (employeeShowOffline || !$(this).hasClass('isOffline') ) {
$(this).removeClass('d-none');
addMarker(markerData);
$("#emp_" + imei).addClass("listItemOn");
}
});
}
})
// Selected departments joblist
$("#jobDepListDropDown").change(function() {
var selectedDep = $('#jobDepListDropDown').val();
if (selectedDep.length > 0) {
// Iterate through each option in the list for employee
$("#jobEmpListDropDown option").each(function() {
// Get the class list of the current <li>
const classList = $(this).attr("class");
// Find the part after "dep_"
// [\w-] matches word characters (letters, digits, underscores) and hyphens (-).
const depMatch = classList.match(/dep_([\w-]+)/);;
if ((depMatch && selectedDep.includes(depMatch[1])) || $(this).hasClass('allDep')) {
$(this).removeClass('d-none');
$(this).prop("disabled", false); // Ensure the option is enabled
} else {
$(this).addClass('d-none');
$(this).prop("disabled", true); // Disable the option to hide it in Chosen
}
});
// Refresh Chosen to reflect the changes for employee
$("#jobEmpListDropDown").trigger("chosen:updated");
} else {
//employee
$("#jobEmpListDropDown option").each(function() {
$(this).removeClass('d-none');
$(this).prop("disabled", false); // Ensure the option is enabled
});
$("#jobEmpListDropDown").trigger("chosen:updated");
}
})
// Selected departments jobcreate
$("#jobCreateDepListDropDown").change(function() {
var selectedDep = $('#jobCreateDepListDropDown').val();
$("#jobCreate-message").addClass("d-none").html( '' );
if (selectedDep.length > 0) {
// Iterate through each option in the list for employee
$("#jobCreateEmpListDropDown option").each(function() {
// Get the class list of the current <li>
const classList = $(this).attr("class");
// Find the part after "dep_"
// [\w-] matches word characters (letters, digits, underscores) and hyphens (-).
const depMatch = classList.match(/dep_([\w-]+)/);;
if ((depMatch && selectedDep.includes(depMatch[1])) || $(this).hasClass('allDep')) {
$(this).removeClass('d-none');
$(this).prop("disabled", false); // Ensure the option is enabled
} else {
$(this).addClass('d-none');
$(this).prop("disabled", true); // Disable the option to hide it in Chosen
}
});
// Refresh Chosen to reflect the changes for employee
$("#jobCreateEmpListDropDown").trigger("chosen:updated");
} else {
//employee
$("#jobCreateEmpListDropDown option").each(function() {
$(this).removeClass('d-none');
$(this).prop("disabled", false); // Ensure the option is enabled
});
$("#jobCreateEmpListDropDown").trigger("chosen:updated");
}
})
//online
$('#employeeShowOffline').change(function() {
const isChecked = $(this).prop('checked');
if (isChecked) {
$('.isOffline').removeClass('d-none');
} else {
$('.isOffline').addClass('d-none');
}
$('.isOffline').each(function() {
var id = this.id;
var idArr = id.split("_");
var imei = idArr[1];
var markerData = carData[imei]["marker"];
if (isChecked) {
addMarker(markerData);
$("#emp_" + imei).addClass("listItemOn");
} else {
removeEmployeeMarker(imei);
$("#emp_" + imei).removeClass("listItemOn");
}
});
})
//hide show routes
$("#showRouteBtn").click(function(){
$(".listIconShowRoute").removeClass("d-none");
$(".listIconHideRoute").addClass("d-none");
$(this).addClass("d-none");
$("#hideRouteBtn").removeClass("d-none");
$("#hideRouteIcon").removeClass("d-none");
removePolyline(mapGlobals.routePolyline);
showRoute();
});
$("#hideRouteBtn").click(function(){
$(".listIconShowRoute").removeClass("d-none");
$(".listIconHideRoute").addClass("d-none");
$(this).addClass("d-none");
$("#showRouteBtn").removeClass("d-none");
$("#showRouteIcon").removeClass("d-none");
removeAllMarkers(mapGlobals.gpsEmpLocationMarkers);
removePolyline(mapGlobals.routePolyline);
removeMarker(mapGlobals.clickedPolyLineMarker);
});
//hide route when modal closes
$("#empInfoModal").on("hidden.bs.modal", function () {
$("#hideRouteBtn").click();
});
//toogle emp modal
$('#collapseModalToggle').on('click', function () {
const collapseBody = $('#collapse-body');
const icon = $(this).find('.toggle-icon');
// Toggle the collapse state
collapseBody.collapse('toggle');
// Change icon based on collapse state
collapseBody.on('shown.bs.collapse', function () {
icon.removeClass('fa-circle-caret-down').addClass('fa-circle-caret-up');
});
collapseBody.on('hidden.bs.collapse', function () {
icon.removeClass('fa-circle-caret-up').addClass('fa-circle-caret-down');
});
});
// Set focus on tabs
$('#home-tab').on('shown.bs.tab', function () {
$("#modal-message").addClass("d-none");
$("#hideRouteBtn").click();
})
$('#day-tab').on('shown.bs.tab', function () {
$("#modal-message").addClass("d-none");
$('#day-dateInput').focus();
})
$('#period-tab').on('shown.bs.tab', function () {
$("#modal-message").addClass("d-none");
$("#hideRouteBtn").click();
$('#period-fromdateInput').focus();
})
//Handle All day in jobcreate
$("#jobCreateDayCheckbox").change(function () {
$("#jobCreate-message").addClass("d-none").html( '' );
if ($(this).is(":checked")) {
var selectedDep = $('#jobCreateDepListDropDown').val();
if (selectedDep == "") {
$("#jobCreate-message").removeClass("d-none").html( '<?php echo $locale_text["ERROR_CHOOSE_DEP"] ?>' );
$("#jobCreateDayCheckbox").prop("checked", false);
$('#jobCreateDepListDropDown').focus();
} else {
const jobDate = $('#jobCreateDateInput').val();
var todayNumber = new Date(jobDate).getDay();
if ( todayNumber == 0 ) todayNumber = 6;
else todayNumber = todayNumber - 1;
parmData = 'depUuid='+selectedDep
;
$.ajax({
cache: false,
url: "ajax_get_availability.php",
data: parmData,
dataType: "json",
success: function(jsonDataDetail){
//console.log(jsonDataDetail);
if (jsonDataDetail["error"] != "") {
console.log(jsonDataDetail["error"]);
} else {
delete jsonDataDetail["error"];
const availData = jsonDataDetail[todayNumber];
$('#jobCreateFromtimeInput').val(availData['startTimeOnly']);
$('#jobCreateTotimeInput').val(availData['endTimeOnly']);
}
},
error: function(xhr, status, error) {
console.log(error);
}
});
}
} else {
$("#jobCreate-message").addClass("d-none").html( '' );
}
});
//submit new job
$("#jobCreateForm").submit(function (e) {
e.preventDefault(); // Prevent default form submission
$("#jobCreate-message").addClass("d-none");
const selectedCust = $('#jobCreateModal-custId').val();
const selectedServiceUnit = $('#jobCreateModal-serviceunitId').val();
const selectedDep = $('#jobCreateDepListDropDown').val();
const selectedActType = $('#jobCreateActTypeListDropDown').val();
const selectedEmp = $('#jobCreateEmpListDropDown').val();
const jobName = $('#jobCreateNameInput').val();
const jobDescription = $('#jobCreateDescriptionInput').val();
const jobDate = $('#jobCreateDateInput').val();
const fromTime = $('#jobCreateFromtimeInput').val();
const toTime = $('#jobCreateTotimeInput').val();
// Validate Department and Employee
if (!selectedCust) {
$("#jobCreate-message").removeClass("d-none").html( '<?php echo $locale_text["ERROR_CHOOSE_CUST"] ?>' );
return;
}
if (!selectedDep) {
$("#jobCreate-message").removeClass("d-none").html( '<?php echo $locale_text["ERROR_CHOOSE_DEP"] ?>' );
$('#jobCreateDepListDropDown').focus();
return;
}
if (!selectedActType) {
$("#jobCreate-message").removeClass("d-none").html( '<?php echo $locale_text["ERROR_CHOOSE_ACT"] ?>' );
$('#jobCreateActTypeListDropDown').focus();
return;
}
if (jobName.trim() == "" ) {
$("#jobCreate-message").removeClass("d-none").html( '<?php echo $locale_text["ERROR_CHOOSE_JOBNAME"] ?>' );
$('#jobName').focus();
return;
}
if (!jobDate) {
$("#jobCreate-message").removeClass("d-none").html( '<?php echo $locale_text["ERROR_DATE_WARNING"] ?>' );
$('#jobCreateDateInput').focus();
return;
}
if (!fromTime) {
$("#jobCreate-message").removeClass("d-none").html( '<?php echo $locale_text["ERROR_FROMDATE_WARNING"] ?>' );
$('#jobCreateFromtimeInput').focus();
return;
}
if (!toTime) {
$("#jobCreate-message").removeClass("d-none").html( '<?php echo $locale_text["ERROR_TODATE_WARNING"] ?>' );
$('#jobCreateTotimeInput').focus();
return;
}
if (fromTime > toTime) {
$("#jobCreate-message").removeClass("d-none").html( '<?php echo $locale_text["ERROR_DATE_GREATER"] ?>' );
$('#jobCreateFromtimeInput').focus();
return;
}
// If valid submit
parmData = 'accountId='+selectedCust
+ '&departmentId=' + selectedDep
+ '&activityTypeId=' + selectedActType
+ '&selectedEmp=' + selectedEmp
+ '&selectedServiceUnit=' + selectedServiceUnit
+ '&shortDescription=' + jobName
+ '&longDescription=' + jobDescription
+ '&startDate=' + jobDate
+ '&fromTime=' + fromTime
+ '&toTime=' + toTime
;
$.ajax({
cache: false,
url: "ajax_create_job.php",
data: parmData,
method: 'POST',
dataType: "json",
success: function(jsonData){
//console.log(jsonData);
if (jsonData["error"] != "") {
console.log(jsonData["error"]);
$("#jobCreate-message").removeClass("d-none").html( '<?php echo $locale_text["ERROR_JOBCREATE"] ?>' + ' - ' + jsonData["error"] );
} else {
delete jsonData["error"];
const jobId = jsonData["jobId"];
if (!jobId) {
$("#jobCreate-message").removeClass("d-none").html( '<?php echo $locale_text["ERROR_JOBCREATE"]?>' );
} else {
$('#jobCreateModal').modal('hide'); // Close the modal
}
}
},
error: function(xhr, status, error) {
console.log(error);
}
});
});
});
//=============================================================================
// Constructors
//=============================================================================
// Contruct new MarkerData obj.
function MarkerData(type, latLng, address, id, description, online, latitude, longitude, course, taskStatus, taskPlannedperson, taskUuid){
this.type = type;
this.latLng = latLng;
this.latitude = latitude;
this.longitude = longitude;
this.address = address;
this.id = id;
this.description = description;
this.online = online;
this.course = course;
this.taskStatus = taskStatus;
this.taskPlannedperson = taskPlannedperson;
this.taskUuid = taskUuid;
}
// -->
</script>
</head>
<body>
<div class="container-fluid mt-2">
<div class="card">
<div class="card-header">
<div class="row mt-2">
<div class="col-2 h4">
<img src="images/reeft_logo.png" alt="REEFTwebplanner" />
</div>
<div class="col-8 h2 text-center">
<span class="font-weight-bold"><i class="fa fa-car"> </i><?php echo $locale_text["GPSMAP_gps"] ?> <span id ="loginOrganizationName"></span></span>
</div>
<div class="col-2 text-end">
<span id="toggle-btn" onclick="toggleSidebar()"><?php echo $locale_text["GPSMAP_menu_show"] ?></span>
</div>
</div>
</div>
<div class="card-body" id="mapContainer">
<!-- Sidebar -->
<div class="sidebar overflow-auto" id="sidebar">
<ul class="list-unstyled ps-0">
<!-- Customer -->
<li class="mb-1">
<button class="btn btn-toggle d-flex justify-content-between align-items-center rounded collapsed" data-bs-toggle="collapse" data-bs-target="#customerCollapse" aria-expanded="false">
<span>
<i class="fa fa-user fa-fw me-2"></i><?php echo $locale_text["GPSMAP_customer"]; ?>
</span>
<i class="fa-sharp fa-solid fa-circle-caret-down toggle-icon"></i>
</button>
<div class="collapse sideCollapsible" id="customerCollapse">
<div class="input-group my-3" id="customerSearchWrapper">
<input type="text" id="customerSearchVal" class="form-control" placeholder="<?php echo $locale_text["GPSMAP_writesearch"]; ?>" aria-label="<?php echo $locale_text["GPSMAP_customer"]; ?>" aria-describedby="customerSearchBtn" onkeyup="getCustomerListTimer(false);">
<span class="input-group-text" id="customerSearchBtn"><i class="fa-sharp fa-thin fa-magnifying-glass"></i></span>
</div>
<div id="customerContentWrapper">
<div class="row mt-2">
<div class="col-5">
<?php echo $locale_text["GPSMAP_number"]; ?>
</div>
<div class="col-7 d-flex justify-content-between align-items-center" id="custNumber">
</div>
</div>
<div class="row mt-2">
<div class="col-5">
<?php echo $locale_text["GPSMAP_name"]; ?>
</div>
<div class="col-7" id="custName">
</div>
</div>
<div class="row mt-2">
<div class="col-5">
<?php echo $locale_text["GPSMAP_location"]; ?>
</div>
<div class="col-7" id="custLocation">
</div>
</div>
<div class="row mt-2">
<div class="col-5">
<?php echo $locale_text["GPSMAP_address"]; ?>
</div>
<div class="col-7" id="custAddress">
</div>
</div>
<div class="row mt-2">
<div class="col-5">
<?php echo $locale_text["GPSMAP_zip_city"]; ?>
</div>
<div class="col-7" id="custZipCity">
</div>
</div>
</div>
<div class="border-top my-3"></div>
<div id="referenceSearchWrapper" class="inputWrapperField inputWrapperFieldSearch d-none" title="<?php echo $locale_text["GPSMAP_filter"]; ?>">
<input type='text' class='customInput searchField' id='refFilter' onkeyup='referenceFilter()' style='width: 100%;' placeholder='<?php echo $locale_text["GPSMAP_filter"]; ?>'>
<div class="border-top my-3"></div>
</div>
<div id="refFilterResult" style="display: none;"><?php echo $locale_text["GPSMAP_noData"]; ?></div>
<div id="customerReferenceListContainer" class="contentSection d-none"></div>
<button type="button" id="hideCustomerBtn" class="btn btn-sm btn-secondary d-none"><?php echo $locale_text["GPSMAP_clearBtn"]; ?></button>
<button type="button" id="createCustomerJobBtn" class="btn btn-sm btn-secondary" disabled><?php echo $locale_text["GPSMAP_create_job"]; ?> <i class="fa-regular fa-calendar-circle-plus"></i></button>
</li>
<!-- Employees -->
<li class="border-top my-3"></li>
<li class="mb-1">
<button class="btn btn-toggle d-flex justify-content-between align-items-center rounded collapsed" data-bs-toggle="collapse" data-bs-target="#employeesCollapse" aria-expanded="false">
<span>
<i class="fa-sharp fa-light fa-truck me-2"></i><?php echo $locale_text["GPSMAP_employees"]; ?>
</span>
<i class="fa-sharp fa-solid fa-circle-caret-down toggle-icon"></i>
</button>
<div class="collapse sideCollapsible" id="employeesCollapse">
<div class="form-check form-switch">
<input class="form-check-input" type="checkbox" role="switch" id="employeeShowOffline">
<label class="form-check-label" for="employeeShowOffline"><?php echo $locale_text["GPSMAP_offline_show"]; ?></label>
</div>
<select id="depListDropDown" name="depListDropDown" data-placeholder="<?php echo $locale_text["GPSMAP_department"]; ?>" multiple></select>
<div id="employeeListContainer" class="my-2">
<ul id="employeeList" name="employeeList" class="list-group"></ul>
</div>
<button id="showAllEmpsBtn" type="button" class="btn btn-sm btn-secondary d-none"><?php echo $locale_text["GPSMAP_showAll"] ?></button>
<button id="hideAllEmpsBtn" type="button" class="btn btn-sm btn-secondary"><?php echo $locale_text["GPSMAP_hideAll"] ?></button>
</div>
</li>
<!-- Job -->
<li class="border-top my-3"></li>
<li class="mb-1">
<button class="btn btn-toggle d-flex justify-content-between align-items-center rounded collapsed" data-bs-toggle="collapse" data-bs-target="#jobCollapse" aria-expanded="false">
<span>
<i class="fa-solid fa-square-list me-2"></i><?php echo $locale_text["GPSMAP_job_list"]; ?>
</span>
<i class="fa-sharp fa-solid fa-circle-caret-down toggle-icon"></i>
</button>
<div class="collapse sideCollapsible" id="jobCollapse">
<div class="mb-1"><select id="jobDepListDropDown" name="jobDepListDropDown" data-placeholder="<?php echo $locale_text["GPSMAP_department"]; ?>" multiple></select></div>
<div class="mb-1"><select id="jobEmpListDropDown" name="jobEmpListDropDown" data-placeholder="<?php echo $locale_text["GPSMAP_employees"]; ?>" multiple></select></div>
<div class="mb-1"><select id="jobStatusListDropDown" name="jobStatusListDropDown" data-placeholder="<?php echo $locale_text["GPSMAP_status"]; ?>" multiple></select></div>
<div class="mb-1"><select id="jobActTypeListDropDown" name="jobActTypeListDropDown" data-placeholder="<?php echo $locale_text["GPSMAP_act_type"]; ?>" multiple></select></div>
<div class="mb-1">
<label for="job-fromdateInput" class="col-form-label"><?php echo $locale_text["GPSMAP_period"]; ?></label>
<div class="input-group">
<span class="input-group-text"><?php echo $locale_text["GPSMAP_from"] ?></span>
<input type="date" value="<?php echo date('Y-m-d'); ?>" class="form-control" id="job-fromdateInput">
</div>
</div>
<div class="mb-1">
<div class="input-group">
<span class="input-group-text"><?php echo $locale_text["GPSMAP_to"] ?></span>
<input type="date" value="<?php echo date('Y-m-d', strtotime("+ 1 month")); ?>" class="form-control" id="job-todateInput">
</div>
</div>
<div class="row mb-1">
<div class="col-6">
<button id="getJobBtn" type="button" class="btn btn-sm btn-secondary" onclick="getJoblist()"><?php echo $locale_text["GPSMAP_load_job_list"] ?></button>
</div>
<div class="col-6">
<div class="col-6 form-check form-switch">
<input class="form-check-input" type="checkbox" role="switch" id="jobGetUnassigned">
<label class="form-check-label" for="jobGetUnassigned"><?php echo $locale_text["GPSMAP_include_pool_job"]; ?></label>
</div>
</div>
</div>
<div class="d-none my-2 p-2 font-weight-bold border border-danger border-4 rounded" id="job-message"> </div>
<div id="jobListContainer" class="mb-2">
<div id="jobListSpinner" class="d-none">
<div class="spinner-border spinner-border-sm"aria-hidden="true"></div>
<span role="status"><?php echo $locale_text["GPSMAP_loading"]; ?></span>
</div>
<ul id="mainJobList" name="mainJobList" class="list-group"></ul>
</div>
<button id="showAllJobsBtn" type="button" class="btn btn-sm btn-secondary d-none"><?php echo $locale_text["GPSMAP_showAll"] ?></button>
<button id="hideAllJobsBtn" type="button" class="btn btn-sm btn-secondary d-none"><?php echo $locale_text["GPSMAP_hideAll"] ?></button>
<button id="clearAllJobsBtn" type="button" onclick="clearJoblist()" class="btn btn-sm btn-secondary d-none"><?php echo $locale_text["GPSMAP_clearBtn"] ?></button>
</div>
<li class="border-top my-3"></li>
<!-- Search -->
<li class="mb-1">
<button class="btn btn-toggle d-flex justify-content-between align-items-center rounded collapsed" data-bs-toggle="collapse" data-bs-target="#addressSearchCollapse" aria-expanded="false">
<span>
<i class="fa-solid fa-building-magnifying-glass me-2"></i></i><?php echo $locale_text["GPSMAP_addresssearch"]; ?>
</span>
<i class="fa-sharp fa-solid fa-circle-caret-down toggle-icon"></i>
</button>
<div class="collapse sideCollapsible" id="addressSearchCollapse">
<div class="input-group my-3" id="addressSearchWrapper">
<span class="input-group-text" id="addressClearBtn" onclick="addressClear()" title="<?php echo $locale_text["GPSMAP_clearBtn"]; ?>"><i class="fa-light fa-square-xmark"></i></span>
<input type="text" id="addressSearchVal" class="form-control" placeholder="<?php echo $locale_text["GPSMAP_search"]; ?>" aria-label="<?php echo $locale_text["GPSMAP_addresssearch"]; ?>" aria-describedby="addressSearchBtn">
<span class="input-group-text" id="addressSearchBtn" onclick="addressSearch()"><i class="fa-sharp fa-thin fa-magnifying-glass"></i></span>
</div>
<div id="addressSearchResult" class="resultList" style="display: none; width: 300px;"></div>
</div>
</li>
<li class="border-top my-3"></li>
</ul>
</div>
<!-- Map Container -->
<div class="map-container" id="map"></div>
</div>
</div>
</div>
<!-- Set footer -->
<?php
include "include/footer.php";
?>
<!-- Set footer -->
<!-- ========================================================================== -->
<!-- M O D A L S -->
<!-- ========================================================================== -->
<!-- Modal start - employee info -->
<div class="modal fade" id="empInfoModal" tabindex="-1" data-bs-backdrop="static" aria-labelledby="empInfoModalLabel" aria-hidden="true">
<div class="modal-dialog modal-lg">
<div class="modal-content opacity-85">
<div class="modal-header d-flex justify-content-between align-items-center">
<h5 class="modal-title" id="empInfoModalLabel"><?php echo $locale_text["GPSMAP_info"] ?></h5>
<div class="d-flex align-items-center">
<button type="button" class="btn" id="collapseModalToggle" aria-expanded="true" aria-controls="collapse-body">
<i class="fa-sharp fa-solid fa-circle-caret-up toggle-icon"></i>
</button>
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
</div>
</div>
<div class="collapse show" id="collapse-body">
<div id="empInfoModal-body" class="modal-body overflow-auto" style="max-height: 60vh;">
<input class="d-none" type="text" id="empInfoModal-userId">
<input class="d-none" type="text" id="empInfoModal-deviceId">
<div class="d-none mb-2 p-2 font-weight-bold border border-danger border-4 rounded" id="modal-message"> </div>
<!-- Tabs Navigation -->
<ul class="nav nav-tabs" id="infoTabs" role="tablist">
<li class="nav-item" role="presentation">
<button class="nav-link active" id="home-tab" data-bs-toggle="tab" data-bs-target="#home" type="button" role="tab" aria-controls="home" aria-selected="true"><?php echo $locale_text["GPSMAP_info"] ?></button>
</li>
<li class="nav-item" role="presentation">
<button class="nav-link" id="day-tab" data-bs-toggle="tab" data-bs-target="#day" on type="button" role="tab" aria-controls="day" aria-selected="false"><?php echo $locale_text["GPSMAP_day"] ?></button>
</li>
<li class="nav-item" role="presentation">
<button class="nav-link" id="period-tab" data-bs-toggle="tab" data-bs-target="#period" type="button" role="tab" aria-controls="period" aria-selected="false"><?php echo $locale_text["GPSMAP_period"] ?></button>
</li>
</ul>
<!-- Tabs Info Content -->
<div class="tab-content" id="infoTabContent">
<div class="tab-pane fade show active" id="home" role="tabpanel" aria-labelledby="home-tab">
<div class="row mt-3">
<label for="empTodayJobContainer" class="col-form-label"><?php echo $locale_text["GPSMAP_scheduled_jobs"]; ?></label>
<div id="empTodayJobContainer">
<div class="spinner-border spinner-border-sm"aria-hidden="true"></div>
<span role="status"><?php echo $locale_text["GPSMAP_loading"]; ?></span></div>
</div>
</div>
<!-- Tabs Day Content -->
<div class="tab-pane fade" id="day" role="tabpanel" aria-labelledby="day-tab">
<div class="row mt-3">
<div class="col-2">
<label for="day-dateInput" class="col-form-label"><?php echo $locale_text["GPSMAP_select_date"]; ?></label>
</div>
<div class="col-3">
<input type="date" max="<?php echo date('Y-m-d'); ?>" value="<?php echo date('Y-m-d'); ?>" class="form-control" id="day-dateInput">
</div>
<div class="col-2">
<button id="getrouteBtnDay" type="button" class="btn btn-secondary" onclick="getDayData()"><?php echo $locale_text["GPSMAP_search"] ?></button>
</div>
<div id ="printCol" class="col-5 text-end col-disabled">
<span id="printGPSTransactionsAll"><?php echo $locale_text["GPSMAP_print_all"]; ?> <input type="checkbox" id="printGPSTransactionsAllCheck" name="printGPSTransactionsAllCheck" value="yes">  </span>
<span id="printGPSTransactions" title="<?php echo $locale_text["GPSMAP_print_driving_report"]; ?>"><i class="fa-light fa-print"></i></span>
</div>
</div>
<div class="row mt-3">
<div class="col-4 text-start">
<?php echo $locale_text["GPSMAP_route_stoptime"]; ?>
</div>
<div class="col-4 text-center">
<span id="showRouteBtn" class="routeBtn"><?php echo $locale_text["GPSMAP_showWholeRoute"]; ?> <span id="showRouteIcon" class='listIcon listIconShowRoute cursorPointer' title='<?php echo $locale_text["GPSMAP_showRoute"]; ?>' ><i class="fad fa-route fa-fw" style="color: red;"></i></span></span>
<span id="hideRouteBtn" class="routeBtn d-none"><?php echo $locale_text["GPSMAP_hideWholeRoute"]; ?> <span id="hideRouteIcon" class='listIcon listIconHideRoute cursorPointer' title='<?php echo $locale_text["GPSMAP_hideRoute"]; ?>' ><i class="fad fa-route fa-fw"></i></span>
</div>
<div id ="printExcelCol" class="col-4 text-end col-disabled">
<span class="excelBtn" title="<?php echo $locale_text["GPSMAP_excel"]; ?>"><i class="fa-light fa-file-excel"></i></span>  
</div>
</div>
<div class="row mt-3">
<div id="day-gpsContainer" class="container"></div>
<input id="totalDistanceSummation_hidden" type="hidden">
<input id="totalDurationSummation_hidden" type="hidden">
</div>
<div class="row mt-3">
<label for="day-JobContainer" class="col-form-label"><?php echo $locale_text["GPSMAP_job"]; ?></label>
<div id="day-JobContainer" class="container"></div>
</div>
<div class="row mt-3">
<label for="day-timesheetContainer" class="col-form-label"><?php echo $locale_text["GPSMAP_timesheet"]; ?></label>
<div id="day-timesheetContainer" class="container"></div>
</div>
</div>
<!-- Tabs Period Content -->
<div class="tab-pane fade" id="period" role="tabpanel" aria-labelledby="period-tab">
<div class="row mt-3">
<div class="col-2">
<label for="period-fromdateInput" class="col-form-label"><?php echo $locale_text["GPSMAP_select_period"]; ?></label>
</div>
<div class="col-3">
<input type="date" max="<?php echo date('Y-m-d'); ?>" value="<?php echo date('Y-m-d', strtotime("-2 days")); ?>" class="form-control" id="period-fromdateInput">
</div>
<div class="col-1">
<label for="period-todateInput" class="col-form-label"><?php echo $locale_text["GPSMAP_to"] ?></label>
</div>
<div class="col-3">
<input type="date" max="<?php echo date('Y-m-d'); ?>" value="<?php echo date('Y-m-d', strtotime("-1 days")); ?>" class="form-control" id="period-todateInput">
</div>
<div class="col-2">
<button id="getrouteBtnPeriod" type="button" class="btn btn-secondary" onclick="getRoute('*PERIOD*')"><?php echo $locale_text["GPSMAP_search"] ?></button>
</div>
</div>
<div class="row mt-3">
<div class="col-4 text-start">
<?php echo $locale_text["GPSMAP_route_stoptime"]; ?>
</div>
<div class="col-4 text-center">
</div>
<div id ="printPeriodCol" class="col-4 text-end">
<span class="excelBtn" title="<?php echo $locale_text["GPSMAP_excel"]; ?>"><i class="fa-light fa-file-excel"></i></span>  
<span id="printPeriodGPSTransactions" title="<?php echo $locale_text["GPSMAP_print_driving_report"]; ?>"><i class="fa-light fa-print"></i></span>
</div>
</div>
<div class="row mt-3">
<div id="period-gpsContainer" class="container"></div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
<!-- Modal end -->
<!-- Modal start - jobcreate -->
<div class="modal fade" id="jobCreateModal" tabindex="-1" data-bs-backdrop="static" aria-labelledby="jobCreateModalLabel" aria-hidden="true">
<div class="modal-dialog modal-lg">
<div class="modal-content opacity-85">
<div class="modal-header d-flex justify-content-between align-items-center">
<h5 class="modal-title" id="jobCreateModalLabel"><?php echo $locale_text["GPSMAP_create_job"] ?></h5>
<div class="d-flex align-items-center">
<button type="button" class="btn" id="collapseModalToggle" aria-expanded="true" aria-controls="collapse-body">
<i class="fa-sharp fa-solid fa-circle-caret-up toggle-icon"></i>
</button>
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
</div>
</div>
<div class="collapse show" id="collapse-body">
<div id="jobCreateModal-body" class="modal-body overflow-auto" >
<form id="jobCreateForm">
<input class="d-none" type="text" id="jobCreateModal-custId">
<input class="d-none" type="text" id="jobCreateModal-serviceunitId">
<div class="row mb-3 align-items-center">
<div class="col-md-6">
<label for="jobCreateDepListDropDown" class="form-label"><?php echo $locale_text["GPSMAP_department"] ?></label>
<select id="jobCreateDepListDropDown" name="jobCreateDepListDropDown" class="form-select"> </select>
</div>
<div class="col-md-6">
<label for="jobCreateActTypeListDropDown" class="form-label"><?php echo $locale_text["GPSMAP_act_type"] ?></label>
<select id="jobCreateActTypeListDropDown" name="jobCreateActTypeListDropDown" class="form-select"> </select>
</div>
</div>
<div class="row mb-3 align-items-center">
<div class="col-md-12">
<label for="jobCreateNameInput" class="form-label"><?php echo $locale_text["GPSMAP_jobname"] ?></label>
<input type="text" id="jobCreateNameInput" name="jobCreateNameInput" class="form-control">
</div>
</div>
<div class="row mb-3 align-items-center">
<div class="col-md-12">
<label for="jobCreateDescriptionInput" class="form-label"><?php echo $locale_text["GPSMAP_description"] ?></label>
<textarea id="jobCreateDescriptionInput" name="jobCreateDescriptionInput" class="form-control" rows="3"></textarea>
</div>
</div>
<div class="row mb-3 align-items-center">
<div class="col-md-3">
<label for="jobCreateEmpListDropDown" class="form-label"><?php echo $locale_text["GPSMAP_employee"] ?></label>
<select id="jobCreateEmpListDropDown" name="jobCreateEmpListDropDown" class="form-select"> </select>
</div>
<div class="col-md-3">
<label for="jobCreateDateInput" class="form-label"><?php echo $locale_text["GPSMAP_date"] ?></label>
<input type="date" id="jobCreateDateInput" name="jobCreateDateInput" class="form-control" min="<?php echo date('Y-m-d'); ?>" value="<?php echo date('Y-m-d'); ?>">
</div>
<div class="col-md-2">
<label for="jobCreateFromtimeInput" class="form-label"><?php echo $locale_text["GPSMAP_starttime"] ?></label>
<input type="time" id="jobCreateFromtimeInput" name="jobCreateFromtimeInput" class="form-control">
</div>
<div class="col-md-2">
<label for="jobCreateTotimeInput" class="form-label"><?php echo $locale_text["GPSMAP_endtime"] ?></label>
<input type="time" id="jobCreateTotimeInput" name="jobCreateTotimeInput" class="form-control">
</div>
<div class="col-md-2">
<label for="jobCreateDayCheckbox" class="form-label"><?php echo $locale_text["GPSMAP_allday"] ?></label>
<div class="form-check">
<input class="form-check-input" type="checkbox" id="jobCreateDayCheckbox" name="jobCreateDayCheckbox">
</div>
</div>
</div>
<div class="d-none my-2 p-2 font-weight-bold border border-danger border-4 rounded" id="jobCreate-message"> </div>
<div class="row">
<div class="col">
<button type="submit" class="btn btn-secondary"><?php echo $locale_text["GPSMAP_save"] ?></button>
</div>
</div>
</form>
</div>
</div>
</div>
</div>
</div>
<!-- Modal end -->
<!-- Define initMap as a global function, placed here to ensure everything is read in DOM -->
<script>
window.initMap = function() {
var location = { lat: dftMapCenterLat, lng: dftMapCenterLng };
var mapOptions = {
zoom: dftZoomlevel,
center: location
};
mapGlobals.map = new google.maps.Map(document.getElementById('map'), mapOptions);
overlay = new google.maps.OverlayView();
overlay.draw = function() {};
overlay.setMap(mapGlobals.map);
mapGlobals.infoWin = new google.maps.InfoWindow(); // Popup window for markers
var getGoogleClusterInlineSvg = function (color) {
var encoded = window.btoa('<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" viewBox="-100 -100 200 200"><defs><g id="a" transform="rotate(45)"><path d="M0 47A47 47 0 0 0 47 0L62 0A62 62 0 0 1 0 62Z" fill-opacity="0.7" stroke="#C3C3C3" stroke-width="1"/><path d="M0 67A67 67 0 0 0 67 0L81 0A81 81 0 0 1 0 81Z" fill-opacity="0.5" stroke="#C3C3C3" stroke-width="1"/><path d="M0 86A86 86 0 0 0 86 0L100 0A100 100 0 0 1 0 100Z" fill-opacity="0.3" stroke="#C3C3C3" stroke-width="1"/></g></defs><g fill="' + color + '"><circle r="42" stroke="#C3C3C3" stroke-width="2"/><use xlink:href="#a"/><g transform="rotate(120)"><use xlink:href="#a"/></g><g transform="rotate(240)"><use xlink:href="#a"/></g></g></svg>');
return ('data:image/svg+xml;base64,' + encoded);
};
var cluster_styles = [
{
width: 70,
height: 70,
url: getGoogleClusterInlineSvg('<?php echo $DFT_COLOR_JOB_NOT_ASSIGNED; ?>'),
textSize: 12
},
{
width: 70,
height: 70,
url: getGoogleClusterInlineSvg('<?php echo $DFT_COLOR_JOB_ACTIVE; ?>'),
textSize: 12
},
{
width: 70,
height: 70,
url: getGoogleClusterInlineSvg('<?php echo $DFT_COLOR_JOB_START; ?>'),
textSize: 12
},
{
width: 70,
height: 70,
url: getGoogleClusterInlineSvg('<?php echo $DFT_COLOR_JOB_ONHOLD; ?>'),
textSize: 12
},
{
width: 70,
height: 70,
url: getGoogleClusterInlineSvg('<?php echo $DFT_COLOR_JOB_REVIEW; ?>'),
textSize: 12
},
{
width: 70,
height: 70,
url: getGoogleClusterInlineSvg('<?php echo $DFT_COLOR_JOB_CLOSED; ?>'),
textSize: 12
},
{
width: 70,
height: 70,
url: getGoogleClusterInlineSvg('<?php echo $DFT_COLOR_JOB_INVOICED; ?>'),
textSize: 12
},
{
width: 70,
height: 70,
url: getGoogleClusterInlineSvg('<?php echo $DFT_COLOR_JOB_CANCEL; ?>'),
textSize: 12
},
{
width: 70,
height: 70,
url: getGoogleClusterInlineSvg('#C3C3C3'),
textSize: 12
},
];
var clusterOptions = {
zoomOnClick: false,
averageCenter: true,
styles: cluster_styles,
gridSize: 5,
debug: true // Enable debugging
}
mapGlobals.markerCluster = new MarkerClusterer(mapGlobals.map, [], clusterOptions);
/**
* It's important to remember that this function runs for EACH cluster individually.
* @param {Array} markers Set of markers for this cluster.
* @param {Number} numStyles Number of styles we have to play with (set in clusterOptions).
*/
mapGlobals.markerCluster.setCalculator(function(markers, numStyles){
var index = 0;
var count = markers.length;
var dv = count;
while (dv !== 0) {
dv = parseInt(dv / 10, 10);
index++;
}
index = Math.min(index, numStyles);
//max jobstatus is 7, so 8 indicates only workers
var minStatus = 8;
markers.forEach(function(marker) {
if(marker.taskStatus != ""){
tmpStatus = parseInt(marker.taskStatus);
minStatus = Math.min(status,minStatus);
}
});
return {
text: count,
index: minStatus
};
});
google.maps.event.addListener(mapGlobals.markerCluster, 'clusterclick', function(cluster){
mapGlobals.infoBubble = new InfoBubble({
maxWidth: 300
});
var markers = cluster.getMarkers();
var showEmp = false;
var showJob = false;
var infoTextEmp = "<ul>";
var infoTextJob = "<ul>";
for (i = 0; i < markers.length; i++) {
var markerId = markers[i].id.split("_");
if(markerId[0] == "em") {
showEmp = true;
var init = "";
try {
var init = mapGlobals.JSON_employeeList[markerId[1]].initials;
}
catch(err) {
return;
}
var empName = mapGlobals.JSON_employeeList[markerId[1]].name;
var phone = mapGlobals.JSON_employeeList[markerId[1]].phone;
infoTextEmp += "(" + init + ")" + " " + empName + " " + phone + "<br>";
} else {
showJob = true;
var description = "";
try {
var description = mapGlobals.selectedJobs[markers[i].id].title;
}
catch(err) {
return;
}
var icon = mapGlobals.selectedJobs[markers[i].id].icon;
var iconPath = icon.split(".");
var color = iconPath[0].split("_");
var taskPlannedperson = mapGlobals.selectedJobs[markers[i].id].taskPlannedperson;
var tmpEditLink = "";
if(mapGlobals.userLevel != 1) {
// Planner / admins can change all jobs
tmpEditLink = "onclick=\"return showJobdetails(" +markerId[1]+ ")\"";
} else {
// Regular user may only change own job.
if(mapGlobals.loggedInUser == taskPlannedperson || mapGlobals.loggedInUser == 0) {
tmpEditLink = "onclick=\"return showJobdetails(" +markerId[1]+ ")\"";
} else {
tmpEditLink = "onclick=\"javascript:alert('<?php echo $locale_text["ERROR_EDIT_NOT_ALLOWED"];?>')\"";
}
}
infoTextJob += "<li style='background-color: #"+color[2]+";'><span "+tmpEditLink+" class='markerInfoIcon cursorPointer' title= '<?php echo $locale_text["GPSMAP_job_info"]; ?>' style='padding-top: 16px;'></span><span style='color: #000000; font-weight: bold;'> " + " " + description + "</span></li>";
}
}
infoTextEmp += "</ul>";
infoTextJob += "</ul>";
var markerTmp = new google.maps.Marker({
position: cluster.getCenter(),
draggable: false
});
if (showJob) {
mapGlobals.infoBubble.addTab('<?php echo $locale_text["GPSMAP_job_list"]; ?> ', infoTextJob);
}
if (showEmp) {
mapGlobals.infoBubble.addTab('<?php echo $locale_text["GPSMAP_employees"]; ?> ', infoTextEmp);
}
mapGlobals.infoBubble.open(mapGlobals.map, markerTmp);
});
getCarlocations("**FIRST**");
//call getCarlocations runs every 60 sec
setTimeout(() => {
getCarlocations("**LATEST**");
setInterval(() => getCarlocations("**LATEST**"), 60000);
}, 60000);
}
</script>
</body>
</html>